Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
wasm:
wasm-pack build --target bundler --release gtars-wasm
wasm-pack build --target web --release gtars-wasm
jq '.name = "@databio/gtars" | .repository = {"type": "git", "url": "https://github.com/databio/gtars"}' gtars-wasm/pkg/package.json > tmp.json && mv tmp.json gtars-wasm/pkg/package.json

test:
Expand Down
6 changes: 3 additions & 3 deletions gtars-bbcache/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[package]
name = "gtars-bbcache"
version = "0.5.1"
version = "0.5.2"
edition = "2024"
description = "Rust implementation of bbcache: a caching system for BED files on bedbase.org"
license = "MIT"
repository = "https://github.com/databio/gtars"

[dependencies]
biocrs = "0.1.0"
reqwest = { version = "0.12.15", features = ["blocking"] }
ureq = "3.1.4"
shellexpand = "3"
tabled = "0.20.0"
dirs = "6.0.0"
Expand All @@ -18,7 +18,7 @@ anyhow = { workspace = true }
serde_json = { workspace = true }

# our code
gtars-core = { path = "../gtars-core", version="0.5.1" }
gtars-core = { path = "../gtars-core", version="0.5.3" }

[dev-dependencies]
rstest = "0.26.1"
Expand Down
9 changes: 7 additions & 2 deletions gtars-bbcache/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use anyhow::{Context, Ok, Result, anyhow};
use biocrs::biocache::BioCache;
use biocrs::models::{NewResource, Resource};

use reqwest::blocking::get;
use ureq::get;
use std::fs::{File, create_dir_all, read_dir, remove_dir, remove_file};
use std::io::{BufRead, BufReader, Error, ErrorKind, Write};
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -367,7 +367,12 @@ impl BBClient {
fn download_bedset_data(&self, bedset_id: &str) -> Result<Vec<String>> {
let bedset_url = format!("{}/v1/bedset/{}/bedfiles", self.bedbase_api, bedset_id);

let response = get(&bedset_url)?.text()?;
let response = get(&bedset_url)
.call()
.map_err(|e| anyhow!("Failed to GET {}: {}", bedset_url, e))?
.body_mut()
.read_to_string()
Comment on lines +373 to +374
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .body_mut().read_to_string() pattern is incorrect for ureq. The read_to_string() method from std::io::Read requires a mutable String buffer as an argument (e.g., read_to_string(&mut buffer)), but this code doesn't provide one.

For ureq 3.x, the correct pattern is to use .into_string() which consumes the response and returns Result<String, Error>:

let response = get(&bedset_url)
    .call()
    .map_err(|e| anyhow!("Failed to GET {}: {}", bedset_url, e))?
    .into_string()
    .map_err(|e| anyhow!("Failed to read response body for {}: {}", bedset_url, e))?;

This is more idiomatic, handles errors properly, and avoids the need for the intermediate .body_mut() call.

Suggested change
.body_mut()
.read_to_string()
.into_string()

Copilot uses AI. Check for mistakes.
.map_err(|e| anyhow!("Failed to read response body for {}: {}", bedset_url, e))?;

let json: serde_json::Value = serde_json::from_str(&response)?;

Expand Down
6 changes: 3 additions & 3 deletions gtars-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "gtars-core"
version = "0.5.2"
version = "0.5.3"
edition = "2021"
description = "Core library for gtars: tools for high performance genomic interval analysis"
license = "MIT"
Expand All @@ -9,7 +9,7 @@ repository = "https://github.com/databio/gtars"
[dependencies]
md-5 = "0.10.6"
num-traits = "0.2.19"
reqwest = { version = "0.12.23", features = ["blocking"], optional=true }
ureq = {version = "3.1.4", optional=true}
tokio = "1.47.1"
bigtools = { version = "0.5.6", optional = true }

Expand All @@ -24,4 +24,4 @@ tempfile = "3.21.0"
[features]
default = []
bigbed = ["dep:bigtools"]
http = ["dep:reqwest"]
http = ["dep:ureq"]
7 changes: 3 additions & 4 deletions gtars-core/src/models/region_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ impl TryFrom<String> for RegionSet {
type Error = anyhow::Error;

fn try_from(value: String) -> Result<Self> {
println!("Converting String to Path: {}", value);
// println!("Converting String to Path: {}", value);
RegionSet::try_from(Path::new(&value))
}
}
Expand Down Expand Up @@ -567,14 +567,13 @@ mod tests {
}

#[rstest]
#[ignore = "Failing but low priority for now"]
fn test_open_from_url() {
let file_path = String::from("https://github.com/databio/gtars/raw/refs/heads/master/gtars/tests/data/regionset/dummy.narrowPeak.bed.gz");
let file_path = String::from("https://www.encodeproject.org/files/ENCFF321QPN/@@download/ENCFF321QPN.bed.gz");
assert!(RegionSet::try_from(file_path).is_ok());
}

#[rstest]
#[ignore = "Failing but low priority"]
#[ignore = "Avoid BEDbase dependency in CI"]
fn test_open_from_bedbase() {
let bbid = String::from("6b2e163a1d4319d99bd465c6c78a9741");
let region_set = RegionSet::try_from(bbid);
Expand Down
49 changes: 30 additions & 19 deletions gtars-core/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use std::str::FromStr;

use anyhow::{Context, Result};
use flate2::read::{GzDecoder, MultiGzDecoder};
#[cfg(feature = "http")]
use reqwest::blocking::Client;
use std::error::Error;
#[cfg(feature = "http")]
use ureq::{get, Error as UreqError};

use crate::models::region::Region;

Expand Down Expand Up @@ -133,31 +133,42 @@ pub fn get_dynamic_reader(path: &Path) -> Result<BufReader<Box<dyn Read>>> {
pub fn get_dynamic_reader_from_url(
url: &Path,
) -> Result<BufReader<Box<dyn std::io::Read>>, Box<dyn Error>> {
// Create an HTTP client and fetch the content
let mut url: String = url.to_str().unwrap().to_string();

let is_ftp: bool = url.starts_with("ftp");
let mut url_str = url
.to_str()
.ok_or_else(|| "URL path is not valid UTF-8")?
.to_string();

let is_ftp = url_str.starts_with("ftp://");
if is_ftp {
println!("ftp is not fully implemented. Bugs could appear");
url = url.replacen("ftp://", "http://", 1);
url_str = url_str.replacen("ftp://", "http://", 1);
}

let response = Client::new()
.get(&url)
.send()
.with_context(|| format!("Failed to fetch content from URL: {}", &url))?
.error_for_status()?
.bytes()?;
let is_gzipped = url_str.ends_with(".gz");

// Convert the response into a cursor for reading
let cursor = Cursor::new(response);
// Perform request
let response = match get(&url_str).call() {
Ok(resp) => resp,
Err(UreqError::StatusCode(code)) => {
return Err(format!("HTTP status {} when fetching {}", code, url_str).into())
}
Err(e) => return Err(format!("Request error when fetching {}: {}", url_str, e).into()),
};

// Read the entire HTTP response body into memory as a Vec<u8>
let mut bytes = Vec::new();
response
.into_body()
.into_reader()
.read_to_end(&mut bytes)
.map_err(|e| format!("Failed reading response body from {}: {}", url_str, e))?;

let is_gzipped = url.ends_with(".gz");
let cursor = Cursor::new(bytes);

let reader: Box<dyn std::io::Read> = match is_gzipped {
true => Box::new(GzDecoder::new(cursor)),
false => Box::new(cursor),
let reader: Box<dyn std::io::Read> = if is_gzipped {
Box::new(GzDecoder::new(cursor))
} else {
Box::new(cursor)
};

Ok(BufReader::new(reader))
Expand Down
2 changes: 1 addition & 1 deletion gtars-python/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "gtars-py"
version = "0.5.2"
version = "0.5.3"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
4 changes: 2 additions & 2 deletions gtars/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "gtars"
version = "0.5.1"
version = "0.5.2"
edition = "2024"
description = "Performance critical tools for genomic interval analysis."
homepage = "https://github.com/databio/gtars"
Expand All @@ -12,7 +12,7 @@ categories = ["science::bioinformatics::genomics", "science::bioinformatics"]


[dependencies]
gtars-core = { path = "../gtars-core", optional = true, version="0.5.1", features = ["bigbed", "http"] }
gtars-core = { path = "../gtars-core", optional = true, version="0.5.3", features = ["bigbed"] }
gtars-tokenizers = { path = "../gtars-tokenizers", features = ["huggingface"], optional = true, version="0.5.1" }
gtars-io = { path = "../gtars-io", optional = true, version="0.5.0" }
gtars-refget = { path = "../gtars-refget", optional = true, version="0.5.0" }
Expand Down