noodles_sam/io/indexed_reader/
builder.rs

1use std::{
2    ffi::{OsStr, OsString},
3    fs::File,
4    io::{self, Read},
5    path::{Path, PathBuf},
6};
7
8use noodles_csi::{self as csi, BinningIndex};
9
10use super::IndexedReader;
11
12/// An indexed SAM reader builder.
13#[derive(Default)]
14pub struct Builder {
15    index: Option<Box<dyn BinningIndex>>,
16}
17
18impl Builder {
19    /// Sets an index.
20    ///
21    /// # Examples
22    ///
23    /// ```
24    /// use noodles_csi as csi;
25    /// use noodles_sam::io::indexed_reader::Builder;
26    ///
27    /// let index = csi::Index::default();
28    /// let builder = Builder::default().set_index(index);
29    /// ```
30    pub fn set_index<I>(mut self, index: I) -> Self
31    where
32        I: BinningIndex + 'static,
33    {
34        self.index = Some(Box::new(index));
35        self
36    }
37
38    /// Builds an indexed SAM reader from a path.
39    ///
40    /// # Examples
41    ///
42    /// ```no_run
43    /// use noodles_sam::io::indexed_reader::Builder;
44    /// let reader = Builder::default().build_from_path("sample.sam.gz")?;
45    /// # Ok::<_, std::io::Error>(())
46    /// ```
47    pub fn build_from_path<P>(mut self, src: P) -> io::Result<IndexedReader<File>>
48    where
49        P: AsRef<Path>,
50    {
51        let src = src.as_ref();
52
53        if self.index.is_none() {
54            let index_src = build_index_src(src);
55            let index = csi::fs::read(index_src)?;
56            self.index = Some(Box::new(index));
57        }
58
59        let file = File::open(src)?;
60        self.build_from_reader(file)
61    }
62
63    /// Builds a indexed SAM reader from a reader.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// # use std::io;
69    /// use noodles_csi as csi;
70    /// use noodles_sam::io::indexed_reader::Builder;
71    ///
72    /// let index = csi::Index::default();
73    /// let reader = Builder::default().set_index(index).build_from_reader(io::empty())?;
74    /// # Ok::<_, io::Error>(())
75    /// ```
76    pub fn build_from_reader<R>(self, reader: R) -> io::Result<IndexedReader<R>>
77    where
78        R: Read,
79    {
80        let index = self
81            .index
82            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "missing index"))?;
83
84        Ok(IndexedReader::new(reader, index))
85    }
86}
87
88fn build_index_src<P>(src: P) -> PathBuf
89where
90    P: AsRef<Path>,
91{
92    const EXT: &str = "csi";
93    push_ext(src.as_ref().into(), EXT)
94}
95
96fn push_ext<S>(path: PathBuf, ext: S) -> PathBuf
97where
98    S: AsRef<OsStr>,
99{
100    let mut s = OsString::from(path);
101    s.push(".");
102    s.push(ext);
103    PathBuf::from(s)
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_build_index_src() {
112        assert_eq!(
113            build_index_src("sample.sam.gz"),
114            PathBuf::from("sample.sam.gz.csi")
115        );
116    }
117}