noodles_fasta/io/indexed_reader/
builder.rs

1use std::{
2    ffi::{OsStr, OsString},
3    fs::File,
4    io::{self, BufRead, BufReader},
5    path::{Path, PathBuf},
6};
7
8use noodles_bgzf as bgzf;
9
10use super::IndexedReader;
11use crate::fai;
12
13/// An indexed FASTA reader builder.
14#[derive(Default)]
15pub struct Builder {
16    index: Option<fai::Index>,
17}
18
19impl Builder {
20    /// Sets an index.
21    ///
22    /// # Examples
23    ///
24    /// ```
25    /// use noodles_fasta::{fai, io::indexed_reader::Builder};
26    /// let index = fai::Index::default();
27    /// let builder = Builder::default().set_index(index);
28    /// ```
29    pub fn set_index(mut self, index: fai::Index) -> Self {
30        self.index = Some(index);
31        self
32    }
33
34    /// Builds an indexed FASTA reader from a path.
35    ///
36    /// # Examples
37    ///
38    /// ```no_run
39    /// use noodles_fasta::io::indexed_reader::Builder;
40    /// let reader = Builder::default().build_from_path("reference.fa")?;
41    /// # Ok::<_, std::io::Error>(())
42    /// ```
43    pub fn build_from_path<P>(self, src: P) -> io::Result<IndexedReader<crate::io::BufReader<File>>>
44    where
45        P: AsRef<Path>,
46    {
47        let src = src.as_ref();
48
49        let index = match self.index {
50            Some(index) => index,
51            None => {
52                let index_src = build_index_src(src);
53                fai::fs::read(index_src)?
54            }
55        };
56
57        let reader = match src.extension().and_then(|ext| ext.to_str()) {
58            Some("gz" | "bgz") => bgzf::indexed_reader::Builder::default()
59                .build_from_path(src)
60                .map(crate::io::BufReader::Bgzf)?,
61            _ => File::open(src)
62                .map(BufReader::new)
63                .map(crate::io::BufReader::Uncompressed)?,
64        };
65
66        Ok(IndexedReader::new(reader, index))
67    }
68
69    /// Builds an indexed FASTA reader from a reader.
70    ///
71    /// # Examples
72    ///
73    /// ```
74    /// use noodles_fasta::{fai, io::indexed_reader::Builder};
75    ///
76    /// let index = fai::Index::default();
77    /// let data = [];
78    /// let builder = Builder::default()
79    ///     .set_index(index)
80    ///     .build_from_reader(&data[..])?;
81    /// # Ok::<_, std::io::Error>(())
82    /// ```
83    pub fn build_from_reader<R>(self, reader: R) -> io::Result<IndexedReader<R>>
84    where
85        R: BufRead,
86    {
87        let index = self
88            .index
89            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "missing index"))?;
90
91        Ok(IndexedReader::new(reader, index))
92    }
93}
94
95fn build_index_src<P>(src: P) -> PathBuf
96where
97    P: AsRef<Path>,
98{
99    const EXT: &str = "fai";
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!(build_index_src("ref.fa"), PathBuf::from("ref.fa.fai"));
120    }
121}