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}