noodles_fasta/
repository.rs

1//! Sequence repository and adapters.
2
3mod adapter;
4pub mod adapters;
5
6pub use self::adapter::Adapter;
7
8use std::{
9    collections::HashMap,
10    fmt, io,
11    sync::{Arc, RwLock},
12};
13
14use super::record::Sequence;
15
16struct AdapterCache {
17    adapter: Box<dyn Adapter>,
18    cache: HashMap<Vec<u8>, Sequence>,
19}
20
21/// A caching sequence repository.
22pub struct Repository(Arc<RwLock<AdapterCache>>);
23
24impl Repository {
25    /// Creates a sequence repository.
26    pub fn new<A>(adapter: A) -> Self
27    where
28        A: Adapter + 'static,
29    {
30        Self(Arc::new(RwLock::new(AdapterCache {
31            adapter: Box::new(adapter),
32            cache: HashMap::new(),
33        })))
34    }
35
36    /// Returns the sequence of the given name.
37    pub fn get(&self, name: &[u8]) -> Option<io::Result<Sequence>> {
38        {
39            let lock = self.0.read().unwrap();
40
41            if let Some(sequence) = lock.cache.get(name) {
42                return Some(Ok(sequence.clone()));
43            }
44        }
45
46        let mut lock = self.0.write().unwrap();
47
48        let record = match lock.adapter.get(name)? {
49            Ok(record) => record,
50            Err(e) => return Some(Err(e)),
51        };
52
53        lock.cache
54            .entry(name.into())
55            .or_insert_with(|| record.sequence().clone());
56
57        Some(Ok(record.sequence().clone()))
58    }
59
60    /// Returns the number of cached sequences.
61    pub fn len(&self) -> usize {
62        self.0.read().unwrap().cache.len()
63    }
64
65    /// Returns whether any sequences are cached.
66    pub fn is_empty(&self) -> bool {
67        self.0.read().unwrap().cache.is_empty()
68    }
69
70    /// Clears the sequence cache.
71    pub fn clear(&self) {
72        self.0.write().unwrap().cache.clear();
73    }
74}
75
76impl Clone for Repository {
77    fn clone(&self) -> Self {
78        Self(self.0.clone())
79    }
80}
81
82impl fmt::Debug for Repository {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        f.debug_struct("Repository")
85            .field("cache", &self.0.read().unwrap().cache)
86            .finish()
87    }
88}
89
90impl Default for Repository {
91    fn default() -> Self {
92        Self::new(adapters::Empty::new())
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99    use crate::{
100        record::{Definition, Sequence},
101        Record,
102    };
103
104    #[test]
105    fn test_get() -> io::Result<()> {
106        let sq0 = Record::new(
107            Definition::new("sq0", None),
108            Sequence::from(b"ACGT".to_vec()),
109        );
110        let repository = Repository::new(vec![sq0.clone()]);
111
112        assert_eq!(
113            repository.get(b"sq0").transpose()?,
114            Some(sq0.sequence().clone())
115        );
116        assert_eq!(repository.get(b"sq1").transpose()?, None);
117
118        Ok(())
119    }
120}