gtars_bbcache/lib.rs
1//! # gtars-bbcache
2//!
3//! A Rust implementation of bbcache: a caching system for BED files from [BEDbase.org](https://bedbase.org).
4//!
5//! ## Overview
6//!
7//! `gtars-bbcache` provides an efficient local caching layer for BED files and BED sets retrieved from
8//! the BEDbase API. It handles downloading, storage, and retrieval of genomic region data.
9//!
10//! ## Features
11//!
12//! - **Smart Caching**: Automatically downloads and caches BED files from BEDbase API on first access
13//! - **BEDset Support**: Manages collections of related BED files as coherent sets
14//! - **Local File Import**: Add local BED files to the cache for unified access
15//! - **Efficient Storage**: Organizes cached files in a hierarchical directory structure
16//! - **SQLite Tracking**: Uses biocache for fast lookups and resource management
17//! - **Configurable**: Customize cache location and API endpoints via environment variables
18//!
19//! ## Quick Start
20//!
21//! ```rust,no_run
22//! use gtars_bbcache::client::BBClient;
23//! use std::path::PathBuf;
24//!
25//! # fn main() -> anyhow::Result<()> {
26//! // Create a client with default settings
27//! let mut client = BBClient::builder().finish()?;
28//!
29//! // Load a BED file from BEDbase (downloads and caches if not present)
30//! let region_set = client.load_bed("6b2e163a1d4319d99bd465c6c78a9741")?;
31//!
32//! // Add a local BED file to the cache
33//! let bed_id = client.add_local_bed_to_cache(
34//! PathBuf::from("path/to/file.bed.gz"),
35//! None
36//! )?;
37//!
38//! // Check if a file exists in cache
39//! let cached_path = client.seek(&bed_id)?;
40//! # Ok(())
41//! # }
42//! ```
43//!
44//! ## Configuration
45//!
46//! The cache behavior can be configured through environment variables:
47//!
48//! - `BBCLIENT_CACHE`: Custom cache directory (default: `~/.bbcache/`)
49//! - `BEDBASE_API`: Custom BEDbase API endpoint (default: `https://api.bedbase.org`)
50//!
51//! Or programmatically via the builder:
52//!
53//! ```rust,no_run
54//! use gtars_bbcache::client::BBClient;
55//! use std::path::PathBuf;
56//!
57//! # fn main() -> anyhow::Result<()> {
58//! let client = BBClient::builder()
59//! .with_cache_folder(PathBuf::from("/custom/cache/path"))
60//! .with_bedbase_api("https://api.bedbase.org".to_string())
61//! .finish()?;
62//! # Ok(())
63//! # }
64//! ```
65
66pub mod client;
67pub mod consts;
68pub mod utils;
69
70#[cfg(test)]
71mod tests {
72 use super::client::BBClient;
73 use rstest::{fixture, rstest};
74 use std::fs::read_dir;
75 use std::path::PathBuf;
76
77 #[fixture]
78 fn path_to_bed_gz_from_bb() -> PathBuf {
79 PathBuf::from(env!("CARGO_MANIFEST_DIR"))
80 .parent()
81 .unwrap()
82 .join("tests/data/6b2e163a1d4319d99bd465c6c78a9741.bed.gz")
83 }
84
85 #[fixture]
86 fn bbid() -> PathBuf {
87 "6b2e163a1d4319d99bd465c6c78a9741".into()
88 }
89
90 #[fixture]
91 fn path_to_bedset() -> PathBuf {
92 PathBuf::from(env!("CARGO_MANIFEST_DIR"))
93 .parent()
94 .unwrap()
95 .join("tests/data/bedset")
96 }
97
98 #[rstest]
99 fn test_bbcache_local(
100 path_to_bed_gz_from_bb: PathBuf,
101 bbid: PathBuf,
102 path_to_bedset: PathBuf,
103 ) -> Result<(), Box<dyn std::error::Error + 'static>> {
104 fn cleaned_subfolders(subfolder: PathBuf) {
105 let subdirs: Vec<_> = read_dir(&subfolder)
106 .unwrap_or_else(|e| {
107 panic!("Failed to read directory {}: {}", subfolder.display(), e)
108 })
109 .filter_map(Result::ok)
110 .filter(|entry| entry.path().is_dir())
111 .collect();
112
113 // Assert no subdirectories exist
114 assert!(
115 subdirs.is_empty(),
116 "Subfolders found in {}: {:?}",
117 subfolder.display(),
118 subdirs.iter().map(|e| e.path()).collect::<Vec<_>>()
119 );
120 }
121 let tempdir = tempfile::tempdir()?;
122 let cache_folder = PathBuf::from(tempdir.path());
123
124 let mut bbc = BBClient::builder()
125 .with_cache_folder(cache_folder.clone())
126 .finish()?;
127
128 let bed_id = bbc
129 .add_local_bed_to_cache(path_to_bed_gz_from_bb, Some(false))
130 .unwrap();
131 assert_eq!(&bed_id, &bbid.to_string_lossy());
132
133 let bedset_id = bbc.add_local_folder_as_bedset(path_to_bedset).unwrap();
134 assert!(bbc.seek(&bedset_id).is_ok());
135
136 bbc.remove(&bedset_id)
137 .expect("Failed to remove bedset file and its bed files");
138 let bedset_subfolder = cache_folder.join("bedsets");
139 cleaned_subfolders(bedset_subfolder);
140
141 bbc.remove(&bbid.to_string_lossy())
142 .expect("Failed to remove cached bed file");
143 let bedfile_subfolder = cache_folder.join("bedfiles");
144 cleaned_subfolders(bedfile_subfolder);
145 Ok(())
146 }
147}