noodles_bcf/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_bgzf as bgzf;
9use noodles_csi::{self as csi, BinningIndex};
10
11use super::IndexedReader;
12
13/// An indexed BCF reader.
14#[derive(Default)]
15pub struct Builder {
16    index: Option<Box<dyn BinningIndex>>,
17}
18
19impl Builder {
20    /// Sets an index.
21    ///
22    /// # Examples
23    ///
24    /// ```
25    /// use noodles_bcf::io::indexed_reader::Builder;
26    /// use noodles_csi as csi;
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 BCF reader from a path.
39    ///
40    /// # Examples
41    ///
42    /// ```no_run
43    /// use noodles_bcf::io::indexed_reader::Builder;
44    /// let reader = Builder::default().build_from_path("sample.bcf")?;
45    /// # Ok::<_, std::io::Error>(())
46    /// ```
47    pub fn build_from_path<P>(self, src: P) -> io::Result<IndexedReader<bgzf::Reader<File>>>
48    where
49        P: AsRef<Path>,
50    {
51        let src = src.as_ref();
52
53        let file = File::open(src)?;
54
55        let index = match self.index {
56            Some(index) => index,
57            None => read_associated_index(src)?,
58        };
59
60        Ok(IndexedReader::new(file, index))
61    }
62
63    /// Builds an indexed BCF reader from a reader.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// # use std::io;
69    /// use noodles_bcf::io::indexed_reader::Builder;
70    /// use noodles_csi as csi;
71    /// let index = csi::Index::default();
72    /// let reader = Builder::default().set_index(index).build_from_reader(io::empty());
73    /// # Ok::<_, io::Error>(())
74    /// ```
75    pub fn build_from_reader<R>(self, reader: R) -> io::Result<IndexedReader<bgzf::Reader<R>>>
76    where
77        R: Read,
78    {
79        let index = self
80            .index
81            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "missing index"))?;
82
83        Ok(IndexedReader::new(reader, index))
84    }
85}
86
87fn read_associated_index<P>(src: P) -> io::Result<Box<dyn BinningIndex>>
88where
89    P: AsRef<Path>,
90{
91    let index = csi::fs::read(build_index_src(src))?;
92    Ok(Box::new(index))
93}
94
95fn build_index_src<P>(src: P) -> PathBuf
96where
97    P: AsRef<Path>,
98{
99    const EXT: &str = "csi";
100    push_ext(src.as_ref().into(), EXT)
101}
102
103fn push_ext<S>(path: PathBuf, ext: S) -> PathBuf
104where
105    S: AsRef<OsStr>,
106{
107    let mut s = OsString::from(path);
108    s.push(".");
109    s.push(ext);
110    PathBuf::from(s)
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_build_index_src() {
119        assert_eq!(
120            build_index_src("sample.bcf"),
121            PathBuf::from("sample.bcf.csi")
122        );
123    }
124}