noodles_gff/feature/record_buf/
builder.rs

1use noodles_core::Position;
2
3use bstr::BString;
4
5use super::{Attributes, RecordBuf};
6use crate::feature::record::{Phase, Strand};
7
8/// A GFF record builder.
9#[derive(Debug)]
10pub struct Builder {
11    reference_sequence_name: BString,
12    source: BString,
13    ty: BString,
14    start: Position,
15    end: Position,
16    score: Option<f32>,
17    strand: Strand,
18    phase: Option<Phase>,
19    attributes: Attributes,
20}
21
22impl Builder {
23    /// Creates a GFF record builder.
24    ///
25    /// Typically, [`RecordBuf::builder`] is used instead of calling this.
26    ///
27    /// # Examples
28    ///
29    /// ```
30    /// use noodles_gff as gff;
31    /// let builder = gff::feature::RecordBuf::builder();
32    /// ```
33    pub fn new() -> Self {
34        Self::default()
35    }
36
37    /// Sets a GFF record reference sequence name.
38    ///
39    /// # Examples
40    ///
41    /// ```
42    /// use noodles_gff as gff;
43    ///
44    /// let record = gff::feature::RecordBuf::builder()
45    ///     .set_reference_sequence_name("sq0")
46    ///     .build();
47    ///
48    /// assert_eq!(record.reference_sequence_name(), "sq0");
49    /// ```
50    pub fn set_reference_sequence_name<N>(mut self, reference_sequence_name: N) -> Self
51    where
52        N: Into<BString>,
53    {
54        self.reference_sequence_name = reference_sequence_name.into();
55        self
56    }
57
58    /// Sets a GFF record source.
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// use noodles_gff as gff;
64    ///
65    /// let record = gff::feature::RecordBuf::builder()
66    ///     .set_source("NOODLES")
67    ///     .build();
68    ///
69    /// assert_eq!(record.source(), "NOODLES");
70    /// ```
71    pub fn set_source<S>(mut self, source: S) -> Self
72    where
73        S: Into<BString>,
74    {
75        self.source = source.into();
76        self
77    }
78
79    /// Sets a GFF record feature type.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// use noodles_gff as gff;
85    ///
86    /// let record = gff::feature::RecordBuf::builder()
87    ///     .set_type("gene")
88    ///     .build();
89    ///
90    /// assert_eq!(record.ty(), "gene");
91    /// ```
92    pub fn set_type<T>(mut self, ty: T) -> Self
93    where
94        T: Into<BString>,
95    {
96        self.ty = ty.into();
97        self
98    }
99
100    /// Sets a GFF record start position.
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// use noodles_core::Position;
106    /// use noodles_gff as gff;
107    /// let start = Position::MIN;
108    /// let record = gff::feature::RecordBuf::builder().set_start(start).build();
109    /// assert_eq!(record.start(), start);
110    /// ```
111    pub fn set_start(mut self, start: Position) -> Self {
112        self.start = start;
113        self
114    }
115
116    /// Sets a GFF record end position.
117    ///
118    /// # Examples
119    ///
120    /// ```
121    /// use noodles_core::Position;
122    /// use noodles_gff as gff;
123    /// let end = Position::MIN;
124    /// let record = gff::feature::RecordBuf::builder().set_end(end).build();
125    /// assert_eq!(record.end(), end);
126    /// ```
127    pub fn set_end(mut self, end: Position) -> Self {
128        self.end = end;
129        self
130    }
131
132    /// Sets a GFF record score.
133    ///
134    /// # Examples
135    ///
136    /// ```
137    /// use noodles_gff as gff;
138    /// let record = gff::feature::RecordBuf::builder().set_score(21.0).build();
139    /// assert_eq!(record.score(), Some(21.0));
140    /// ```
141    pub fn set_score(mut self, score: f32) -> Self {
142        self.score = Some(score);
143        self
144    }
145
146    /// Sets a GFF record strand.
147    ///
148    /// # Examples
149    ///
150    /// ```
151    /// use noodles_gff::{self as gff, feature::record::Strand};
152    ///
153    /// let record = gff::feature::RecordBuf::builder()
154    ///     .set_strand(Strand::Forward)
155    ///     .build();
156    ///
157    /// assert_eq!(record.strand(), Strand::Forward);
158    /// ```
159    pub fn set_strand(mut self, strand: Strand) -> Self {
160        self.strand = strand;
161        self
162    }
163
164    /// Sets a GFF record phase.
165    ///
166    /// # Examples
167    ///
168    /// ```
169    /// use noodles_gff::{self as gff, feature::record::Phase};
170    /// let record = gff::feature::RecordBuf::builder().set_phase(Phase::Zero).build();
171    /// assert_eq!(record.phase(), Some(Phase::Zero));
172    /// ```
173    pub fn set_phase(mut self, phase: Phase) -> Self {
174        self.phase = Some(phase);
175        self
176    }
177
178    /// Sets GFF record attributes.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// use noodles_gff::{
184    ///     self as gff,
185    ///     feature::record_buf::{
186    ///         attributes::field::{Tag, Value},
187    ///         Attributes,
188    ///     },
189    /// };
190    ///
191    /// let attributes: Attributes = [(Tag::from("gene_id"), Value::from("ndls0"))]
192    ///     .into_iter()
193    ///     .collect();
194    ///
195    /// let record = gff::feature::RecordBuf::builder()
196    ///     .set_attributes(attributes.clone())
197    ///     .build();
198    ///
199    /// assert_eq!(record.attributes(), &attributes);
200    /// ```
201    pub fn set_attributes(mut self, attributes: Attributes) -> Self {
202        self.attributes = attributes;
203        self
204    }
205
206    /// Builds a GFF record.
207    ///
208    /// # Example
209    ///
210    /// ```
211    /// use noodles_gff as gff;
212    /// let record = gff::feature::RecordBuf::builder().build();
213    /// ```
214    pub fn build(self) -> RecordBuf {
215        RecordBuf {
216            reference_sequence_name: self.reference_sequence_name,
217            source: self.source,
218            ty: self.ty,
219            start: self.start,
220            end: self.end,
221            score: self.score,
222            strand: self.strand,
223            phase: self.phase,
224            attributes: self.attributes,
225        }
226    }
227}
228
229impl Default for Builder {
230    fn default() -> Self {
231        const MISSING: &str = ".";
232
233        Self {
234            reference_sequence_name: MISSING.into(),
235            source: MISSING.into(),
236            ty: MISSING.into(),
237            start: Position::MIN,
238            end: Position::MIN,
239            score: None,
240            strand: Strand::default(),
241            phase: None,
242            attributes: Attributes::default(),
243        }
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250
251    #[test]
252    fn test_default() {
253        let record = Builder::default();
254
255        assert_eq!(record.reference_sequence_name, ".");
256        assert_eq!(record.source, ".");
257        assert_eq!(record.ty, ".");
258        assert_eq!(record.start, Position::MIN);
259        assert_eq!(record.end, Position::MIN);
260        assert!(record.score.is_none());
261        assert_eq!(record.strand, Strand::default());
262        assert!(record.phase.is_none());
263        assert!(record.attributes.is_empty());
264    }
265
266    #[test]
267    fn test_build() -> Result<(), noodles_core::position::TryFromIntError> {
268        use crate::feature::record_buf::attributes::field::{Tag, Value};
269
270        let attributes: Attributes = [(Tag::from("gene_id"), Value::from("ndls0"))]
271            .into_iter()
272            .collect();
273
274        let record = Builder::new()
275            .set_reference_sequence_name("sq0")
276            .set_source("NOODLES")
277            .set_type("CDS")
278            .set_start(Position::try_from(8)?)
279            .set_end(Position::try_from(13)?)
280            .set_score(21.0)
281            .set_strand(Strand::Forward)
282            .set_phase(Phase::Zero)
283            .set_attributes(attributes.clone())
284            .build();
285
286        assert_eq!(record.reference_sequence_name(), "sq0");
287        assert_eq!(record.source(), "NOODLES");
288        assert_eq!(record.ty(), "CDS");
289        assert_eq!(record.start(), Position::try_from(8)?);
290        assert_eq!(record.end(), Position::try_from(13)?);
291        assert_eq!(record.score(), Some(21.0));
292        assert_eq!(record.strand(), Strand::Forward);
293        assert_eq!(record.phase(), Some(Phase::Zero));
294        assert_eq!(record.attributes(), &attributes);
295
296        Ok(())
297    }
298}