1use std::fmt;
2
3use crate::edges::Edges;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct Record {
8 coverage: u16,
9 cov_fw: u16,
10 cov_rc: u16,
11 edges: Edges,
12}
13
14impl Record {
15 #[must_use]
17 pub fn new(coverage: u16, edges: Option<Edges>) -> Self {
18 Record {
19 coverage,
20 cov_fw: 0,
21 cov_rc: 0,
22 edges: edges.unwrap_or(Edges::empty()),
23 }
24 }
25
26 #[must_use]
36 pub fn coverage(&self) -> u16 {
37 self.coverage
38 }
39
40 #[must_use]
42 pub fn fw_coverage(&self) -> u16 {
43 self.cov_fw
44 }
45
46 #[must_use]
48 pub fn rc_coverage(&self) -> u16 {
49 self.cov_rc
50 }
51
52 #[must_use]
54 pub fn edges(&self) -> Edges {
55 self.edges
56 }
57
58 #[must_use]
60 pub fn incoming_edges(&self) -> Vec<u8> {
61 let mut edges = Vec::new();
62
63 if self.edges.contains(Edges::FLAG_EDGE_IN_A) {
64 edges.push(b'A');
65 }
66 if self.edges.contains(Edges::FLAG_EDGE_IN_C) {
67 edges.push(b'C');
68 }
69 if self.edges.contains(Edges::FLAG_EDGE_IN_G) {
70 edges.push(b'G');
71 }
72 if self.edges.contains(Edges::FLAG_EDGE_IN_T) {
73 edges.push(b'T');
74 }
75
76 edges
77 }
78
79 #[must_use]
81 pub fn outgoing_edges(&self) -> Vec<u8> {
82 let mut edges = Vec::new();
83
84 if self.edges.contains(Edges::FLAG_EDGE_OUT_A) {
85 edges.push(b'A');
86 }
87 if self.edges.contains(Edges::FLAG_EDGE_OUT_C) {
88 edges.push(b'C');
89 }
90 if self.edges.contains(Edges::FLAG_EDGE_OUT_G) {
91 edges.push(b'G');
92 }
93 if self.edges.contains(Edges::FLAG_EDGE_OUT_T) {
94 edges.push(b'T');
95 }
96
97 edges
98 }
99
100 pub fn increment_coverage(&mut self) {
102 self.coverage = self.coverage.saturating_add(1);
103 }
104
105 pub fn increment_fw_coverage(&mut self) {
107 self.cov_fw = self.cov_fw.saturating_add(1);
108 }
109
110 pub fn increment_rc_coverage(&mut self) {
112 self.cov_rc = self.cov_rc.saturating_add(1);
113 }
114
115 pub fn set_coverage(&mut self, coverage: u16) {
117 self.coverage = coverage;
118 }
119
120 pub fn set_incoming_edge(&mut self, nucleotide: u8) {
122 match nucleotide {
123 b'A' => self.edges.insert(Edges::FLAG_EDGE_IN_A),
124 b'C' => self.edges.insert(Edges::FLAG_EDGE_IN_C),
125 b'G' => self.edges.insert(Edges::FLAG_EDGE_IN_G),
126 b'T' => self.edges.insert(Edges::FLAG_EDGE_IN_T),
127 _ => (),
128 }
129 }
130
131 pub fn set_outgoing_edge(&mut self, nucleotide: u8) {
133 match nucleotide {
134 b'A' => self.edges.insert(Edges::FLAG_EDGE_OUT_A),
135 b'C' => self.edges.insert(Edges::FLAG_EDGE_OUT_C),
136 b'G' => self.edges.insert(Edges::FLAG_EDGE_OUT_G),
137 b'T' => self.edges.insert(Edges::FLAG_EDGE_OUT_T),
138 _ => (),
139 }
140 }
141
142 #[must_use]
144 pub fn in_degree(&self) -> u8 {
145 let mut degree: u8 = 0;
146 degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_IN_A));
147 degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_IN_C));
148 degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_IN_G));
149 degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_IN_T));
150 degree
151 }
152
153 #[must_use]
155 pub fn out_degree(&self) -> u8 {
156 let mut degree: u8 = 0;
157 degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_OUT_A));
158 degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_OUT_C));
159 degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_OUT_G));
160 degree += u8::from(self.edges.contains(Edges::FLAG_EDGE_OUT_T));
161 degree
162 }
163
164 #[must_use]
166 pub fn is_junction(&self) -> bool {
167 self.in_degree() > 1 || self.out_degree() > 1
168 }
169}
170
171impl fmt::Display for Record {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 write!(
174 f,
175 "{} {}{}{}{}{}{}{}{}",
176 self.coverage,
177 if self.edges.contains(Edges::FLAG_EDGE_IN_A) {
178 'a'
179 } else {
180 '.'
181 },
182 if self.edges.contains(Edges::FLAG_EDGE_IN_C) {
183 'c'
184 } else {
185 '.'
186 },
187 if self.edges.contains(Edges::FLAG_EDGE_IN_G) {
188 'g'
189 } else {
190 '.'
191 },
192 if self.edges.contains(Edges::FLAG_EDGE_IN_T) {
193 't'
194 } else {
195 '.'
196 },
197 if self.edges.contains(Edges::FLAG_EDGE_OUT_A) {
198 'A'
199 } else {
200 '.'
201 },
202 if self.edges.contains(Edges::FLAG_EDGE_OUT_C) {
203 'C'
204 } else {
205 '.'
206 },
207 if self.edges.contains(Edges::FLAG_EDGE_OUT_G) {
208 'G'
209 } else {
210 '.'
211 },
212 if self.edges.contains(Edges::FLAG_EDGE_OUT_T) {
213 'T'
214 } else {
215 '.'
216 },
217 )
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 #[test]
227 fn test_get_coverage() {
228 let r1 = Record::new(1, None);
229 let r10 = Record::new(10, None);
230
231 assert!(r1.coverage() == 1);
232 assert!(r10.coverage() == 10);
233 }
234
235 #[test]
236 fn test_set_coverage() {
237 let mut r100 = Record::new(0, None);
238 r100.set_coverage(100);
239
240 let mut r1000 = Record::new(0, None);
241 r1000.set_coverage(1000);
242
243 assert!(r100.coverage() == 100);
244 assert!(r1000.coverage() == 1000);
245 }
246
247 #[test]
248 fn test_increment_coverage() {
249 let mut r100 = Record::new(99, None);
250 r100.increment_coverage();
251
252 let mut r1000 = Record::new(999, None);
253 r1000.increment_coverage();
254
255 assert!(r100.coverage() == 100);
256 assert!(r1000.coverage() == 1000);
257 }
258
259 #[test]
260 fn test_increment_coverage_saturates() {
261 let mut rmax = Record::new(u16::MAX, None);
262 rmax.increment_coverage();
263
264 assert!(rmax.coverage() == u16::MAX);
265 }
266
267 #[test]
268 fn test_set_incoming_edge() {
269 let mut r = Record::new(1, None);
270
271 assert!(r.edges.is_empty());
272
273 r.set_incoming_edge('A' as u8);
274 assert!(r.edges.contains(Edges::FLAG_EDGE_IN_A));
275 assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_C));
276 assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_G));
277 assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_T));
278
279 r.set_incoming_edge('C' as u8);
280 assert!(r.edges.contains(Edges::FLAG_EDGE_IN_A));
281 assert!(r.edges.contains(Edges::FLAG_EDGE_IN_C));
282 assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_G));
283 assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_T));
284
285 r.set_incoming_edge('G' as u8);
286 assert!(r.edges.contains(Edges::FLAG_EDGE_IN_A));
287 assert!(r.edges.contains(Edges::FLAG_EDGE_IN_C));
288 assert!(r.edges.contains(Edges::FLAG_EDGE_IN_G));
289 assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_T));
290
291 r.set_incoming_edge('T' as u8);
292 assert!(r.edges.contains(Edges::FLAG_EDGE_IN_A));
293 assert!(r.edges.contains(Edges::FLAG_EDGE_IN_C));
294 assert!(r.edges.contains(Edges::FLAG_EDGE_IN_G));
295 assert!(r.edges.contains(Edges::FLAG_EDGE_IN_T));
296
297 assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_A));
298 assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_C));
299 assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_G));
300 assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_T));
301 }
302
303 #[test]
304 fn test_set_outgoing_edge() {
305 let mut r = Record::new(1, None);
306
307 assert!(r.edges.is_empty());
308
309 r.set_outgoing_edge('A' as u8);
310 assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_A));
311 assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_C));
312 assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_G));
313 assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_T));
314
315 r.set_outgoing_edge('C' as u8);
316 assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_A));
317 assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_C));
318 assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_G));
319 assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_T));
320
321 r.set_outgoing_edge('G' as u8);
322 assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_A));
323 assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_C));
324 assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_G));
325 assert!(!r.edges.contains(Edges::FLAG_EDGE_OUT_T));
326
327 r.set_outgoing_edge('T' as u8);
328 assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_A));
329 assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_C));
330 assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_G));
331 assert!(r.edges.contains(Edges::FLAG_EDGE_OUT_T));
332
333 assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_A));
334 assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_C));
335 assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_G));
336 assert!(!r.edges.contains(Edges::FLAG_EDGE_IN_T));
337 }
338
339 #[test]
340 fn test_in_degree() {
341 let mut r = Record::new(1, None);
342
343 assert!(r.in_degree() == 0);
344
345 r.set_incoming_edge('A' as u8);
346 assert!(r.in_degree() == 1);
347
348 r.set_incoming_edge('C' as u8);
349 assert!(r.in_degree() == 2);
350
351 r.set_incoming_edge('G' as u8);
352 assert!(r.in_degree() == 3);
353
354 r.set_incoming_edge('T' as u8);
355 assert!(r.in_degree() == 4);
356
357 r.set_outgoing_edge('A' as u8);
358 assert!(r.in_degree() == 4);
359 }
360
361 #[test]
362 fn test_out_degree() {
363 let mut r = Record::new(1, None);
364
365 assert!(r.out_degree() == 0);
366
367 r.set_outgoing_edge('A' as u8);
368 assert!(r.out_degree() == 1);
369
370 r.set_outgoing_edge('C' as u8);
371 assert!(r.out_degree() == 2);
372
373 r.set_outgoing_edge('G' as u8);
374 assert!(r.out_degree() == 3);
375
376 r.set_outgoing_edge('T' as u8);
377 assert!(r.out_degree() == 4);
378
379 r.set_outgoing_edge('A' as u8);
380 assert!(r.out_degree() == 4);
381 }
382
383 #[test]
384 fn test_incoming_junction() {
385 let mut r = Record::new(1, None);
386
387 assert!(r.is_junction() == false);
388
389 r.set_incoming_edge('A' as u8);
390 assert!(r.is_junction() == false);
391
392 r.set_incoming_edge('C' as u8);
393 assert!(r.is_junction() == true);
394 }
395
396 #[test]
397 fn test_outgoing_junction() {
398 let mut r = Record::new(1, None);
399
400 assert!(r.is_junction() == false);
401
402 r.set_outgoing_edge('A' as u8);
403 assert!(r.is_junction() == false);
404
405 r.set_outgoing_edge('C' as u8);
406 assert!(r.is_junction() == true);
407 }
408}