noodles_sam/header/
builder.rs

1use bstr::BString;
2
3use super::{
4    record::value::{
5        map::{self, Program, ReadGroup, ReferenceSequence},
6        Map,
7    },
8    Header, Programs, ReadGroups, ReferenceSequences,
9};
10
11/// A SAM header builder.
12#[derive(Debug, Default)]
13pub struct Builder {
14    header: Option<Map<map::Header>>,
15    reference_sequences: ReferenceSequences,
16    read_groups: ReadGroups,
17    programs: Programs,
18    comments: Vec<BString>,
19}
20
21impl Builder {
22    /// Sets a SAM header header.
23    ///
24    /// # Examples
25    ///
26    /// ```
27    /// use noodles_sam as sam;
28    ///
29    /// let header = sam::Header::builder()
30    ///     .set_header(Default::default())
31    ///     .build();
32    ///
33    /// assert!(header.header().is_some());
34    /// ```
35    pub fn set_header(mut self, header: Map<map::Header>) -> Self {
36        self.header = Some(header);
37        self
38    }
39
40    /// Sets the reference sequences.
41    ///
42    /// # Examples
43    ///
44    /// ```
45    /// use std::num::NonZeroUsize;
46    ///
47    /// use bstr::BString;
48    /// use noodles_sam::{
49    ///     self as sam,
50    ///     header::record::value::{map::ReferenceSequence, Map},
51    /// };
52    ///
53    /// let reference_sequences = [(
54    ///     BString::from("sq0"),
55    ///     Map::<ReferenceSequence>::new(NonZeroUsize::try_from(13)?),
56    /// )]
57    /// .into_iter()
58    /// .collect();
59    ///
60    /// let header = sam::Header::builder()
61    ///     .set_reference_sequences(reference_sequences)
62    ///     .build();
63    ///
64    /// let reference_sequences = header.reference_sequences();
65    /// assert_eq!(reference_sequences.len(), 1);
66    /// assert!(reference_sequences.contains_key(&b"sq0"[..]));
67    /// # Ok::<(), std::num::TryFromIntError>(())
68    /// ```
69    pub fn set_reference_sequences(mut self, reference_sequences: ReferenceSequences) -> Self {
70        self.reference_sequences = reference_sequences;
71        self
72    }
73
74    /// Adds a reference sequence to the SAM header.
75    ///
76    /// # Examples
77    ///
78    /// ```
79    /// use std::num::NonZeroUsize;
80    ///
81    /// use noodles_sam::{
82    ///     self as sam,
83    ///     header::record::value::{map::ReferenceSequence, Map},
84    /// };
85    ///
86    /// let header = sam::Header::builder()
87    ///     .add_reference_sequence(
88    ///         "sq0",
89    ///         Map::<ReferenceSequence>::new(NonZeroUsize::try_from(13)?),
90    ///     )
91    ///     .build();
92    ///
93    /// let reference_sequences = header.reference_sequences();
94    /// assert_eq!(reference_sequences.len(), 1);
95    /// assert!(reference_sequences.contains_key(&b"sq0"[..]));
96    /// # Ok::<(), Box<dyn std::error::Error>>(())
97    /// ```
98    pub fn add_reference_sequence<N>(
99        mut self,
100        name: N,
101        reference_sequence: Map<ReferenceSequence>,
102    ) -> Self
103    where
104        N: Into<BString>,
105    {
106        self.reference_sequences
107            .insert(name.into(), reference_sequence);
108
109        self
110    }
111
112    /// Adds a read group to the SAM header.
113    ///
114    /// # Examples
115    ///
116    /// ```
117    /// use noodles_sam::{
118    ///     self as sam,
119    ///     header::record::value::{map::ReadGroup, Map},
120    /// };
121    ///
122    /// let header = sam::Header::builder()
123    ///     .add_read_group("rg0", Map::<ReadGroup>::default())
124    ///     .build();
125    ///
126    /// let read_groups = header.read_groups();
127    /// assert_eq!(read_groups.len(), 1);
128    /// assert!(read_groups.contains_key(&b"rg0"[..]));
129    /// ```
130    pub fn add_read_group<I>(mut self, id: I, map: Map<ReadGroup>) -> Self
131    where
132        I: Into<BString>,
133    {
134        self.read_groups.insert(id.into(), map);
135        self
136    }
137
138    /// Adds a program to the SAM header.
139    ///
140    /// # Examples
141    ///
142    /// ```
143    /// use noodles_sam::{self as sam, header::record::value::{map::Program, Map}};
144    ///
145    /// let header = sam::Header::builder()
146    ///     .add_program("noodles-sam", Map::<Program>::default())
147    ///     .build();
148    ///
149    /// let programs = header.programs();
150    /// assert_eq!(programs.as_ref().len(), 1);
151    /// assert!(programs.as_ref().contains_key(&b"noodles-sam"[..]));
152    /// ```
153    pub fn add_program<I>(mut self, id: I, map: Map<Program>) -> Self
154    where
155        I: Into<BString>,
156    {
157        self.programs.as_mut().insert(id.into(), map);
158        self
159    }
160
161    /// Adds a comment to the SAM header.
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// use noodles_sam as sam;
167    /// let header = sam::Header::builder().add_comment("noodles-sam").build();
168    /// let comments = header.comments();
169    /// assert_eq!(comments.len(), 1);
170    /// assert_eq!(&comments[0], &b"noodles-sam"[..]);
171    /// ```
172    pub fn add_comment<C>(mut self, comment: C) -> Self
173    where
174        C: Into<BString>,
175    {
176        self.comments.push(comment.into());
177        self
178    }
179
180    /// Builds a SAM header.
181    ///
182    /// # Examples
183    ///
184    /// ```
185    /// use noodles_sam as sam;
186    /// let header = sam::Header::builder().build();
187    /// assert!(header.is_empty());
188    /// ```
189    pub fn build(self) -> Header {
190        Header {
191            header: self.header,
192            reference_sequences: self.reference_sequences,
193            read_groups: self.read_groups,
194            programs: self.programs,
195            comments: self.comments,
196        }
197    }
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    #[test]
205    fn test_default() {
206        let header = Builder::default();
207
208        assert!(header.header.is_none());
209        assert!(header.reference_sequences.is_empty());
210        assert!(header.read_groups.is_empty());
211        assert!(header.programs.as_ref().is_empty());
212        assert!(header.comments.is_empty());
213    }
214
215    #[test]
216    fn test_build() -> Result<(), Box<dyn std::error::Error>> {
217        use std::num::NonZeroUsize;
218
219        let header = Builder::default()
220            .add_reference_sequence(
221                "sq0",
222                Map::<ReferenceSequence>::new(NonZeroUsize::try_from(8)?),
223            )
224            .add_reference_sequence(
225                "sq1",
226                Map::<ReferenceSequence>::new(NonZeroUsize::try_from(13)?),
227            )
228            .add_reference_sequence(
229                "sq2",
230                Map::<ReferenceSequence>::new(NonZeroUsize::try_from(21)?),
231            )
232            .add_read_group("rg0", Map::<ReadGroup>::default())
233            .add_read_group("rg1", Map::<ReadGroup>::default())
234            .add_program("pg0", Map::<Program>::default())
235            .add_comment("written by noodles-sam")
236            .build();
237
238        let reference_sequences = header.reference_sequences();
239        assert_eq!(reference_sequences.len(), 3);
240        assert!(reference_sequences.contains_key(&b"sq0"[..]));
241        assert!(reference_sequences.contains_key(&b"sq1"[..]));
242        assert!(reference_sequences.contains_key(&b"sq2"[..]));
243
244        assert_eq!(header.read_groups().len(), 2);
245
246        assert_eq!(header.programs().as_ref().len(), 1);
247
248        let comments = header.comments();
249        assert_eq!(comments.len(), 1);
250        assert_eq!(&comments[0], &b"written by noodles-sam"[..]);
251
252        Ok(())
253    }
254}