noodles_cram/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_fasta as fasta;
9
10use super::IndexedReader;
11use crate::crai;
12
13/// An indexed CRAM reader builder.
14#[derive(Default)]
15pub struct Builder {
16    reference_sequence_repository: fasta::Repository,
17    index: Option<crai::Index>,
18}
19
20impl Builder {
21    /// Sets the reference sequence repository.
22    ///
23    /// # Examples
24    ///
25    /// ```
26    /// use noodles_cram::io::indexed_reader::Builder;
27    /// use noodles_fasta as fasta;
28    ///
29    /// let reference_sequence_repository = fasta::Repository::default();
30    /// let builder = Builder::default()
31    ///     .set_reference_sequence_repository(reference_sequence_repository);
32    /// ```
33    pub fn set_reference_sequence_repository(
34        mut self,
35        reference_sequence_repository: fasta::Repository,
36    ) -> Self {
37        self.reference_sequence_repository = reference_sequence_repository;
38        self
39    }
40
41    /// Sets an index.
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// use noodles_cram::{crai, io::indexed_reader::Builder};
47    /// let index = crai::Index::default();
48    /// let builder = Builder::default().set_index(index);
49    /// ```
50    pub fn set_index(mut self, index: crai::Index) -> Self {
51        self.index = Some(index);
52        self
53    }
54
55    /// Builds an indexed CRAM reader from a path.
56    ///
57    /// If no index is set, this will attempt to read an associated index at `<src>.crai`.
58    ///
59    /// # Examples
60    ///
61    /// ```no_run
62    /// use noodles_cram::io::indexed_reader::Builder;
63    /// let builder = Builder::default().build_from_path("sample.cram")?;
64    /// # Ok::<_, std::io::Error>(())
65    /// ```
66    pub fn build_from_path<P>(mut self, src: P) -> io::Result<IndexedReader<File>>
67    where
68        P: AsRef<Path>,
69    {
70        let src = src.as_ref();
71
72        if self.index.is_none() {
73            let index_src = build_index_src(src);
74            self.index = crai::fs::read(index_src).map(Some)?;
75        }
76
77        let file = File::open(src)?;
78        self.build_from_reader(file)
79    }
80
81    /// Builds an indexed CRAM reader from a reader.
82    ///
83    /// # Examples
84    ///
85    /// ```
86    /// # use std::io;
87    /// use noodles_cram::{crai, io::indexed_reader::Builder};
88    ///
89    /// let index = crai::Index::default();
90    /// let builder = Builder::default()
91    ///     .set_index(index)
92    ///     .build_from_reader(io::empty())?;
93    /// # Ok::<_, std::io::Error>(())
94    /// ```
95    pub fn build_from_reader<R>(self, reader: R) -> io::Result<IndexedReader<R>>
96    where
97        R: Read,
98    {
99        let inner = crate::io::reader::Builder::default()
100            .set_reference_sequence_repository(self.reference_sequence_repository)
101            .build_from_reader(reader);
102
103        let index = self
104            .index
105            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "missing index"))?;
106
107        Ok(IndexedReader { inner, index })
108    }
109}
110
111fn build_index_src<P>(src: P) -> PathBuf
112where
113    P: AsRef<Path>,
114{
115    const EXT: &str = "crai";
116    push_ext(src.as_ref().into(), EXT)
117}
118
119fn push_ext<S>(path: PathBuf, ext: S) -> PathBuf
120where
121    S: AsRef<OsStr>,
122{
123    let mut s = OsString::from(path);
124    s.push(".");
125    s.push(ext);
126    PathBuf::from(s)
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn test_build_index_src() {
135        assert_eq!(
136            build_index_src("sample.cram"),
137            PathBuf::from("sample.cram.crai")
138        );
139    }
140}