noodles_sam/record/data/
field.rs

1//! SAM record data field and components.
2
3mod tag;
4mod ty;
5pub mod value;
6
7use std::io;
8
9use self::ty::Type;
10use self::{tag::parse_tag, ty::parse_type, value::parse_value};
11use crate::alignment::record::data::field::{Tag, Value};
12
13pub(super) fn parse_field<'a>(src: &mut &'a [u8]) -> io::Result<(Tag, Value<'a>)> {
14    let tag = parse_tag(src)?;
15    consume_delimiter(src)?;
16    let ty = parse_type(src)?;
17    consume_delimiter(src)?;
18    let value = parse_value(src, ty)?;
19    maybe_consume_terminator(src)?;
20
21    Ok((tag, value))
22}
23
24fn consume_delimiter(src: &mut &[u8]) -> io::Result<()> {
25    const DELIMITER: u8 = b':';
26
27    if let Some((b, rest)) = src.split_first() {
28        if *b == DELIMITER {
29            *src = rest;
30            Ok(())
31        } else {
32            Err(io::Error::new(
33                io::ErrorKind::InvalidData,
34                "invalid delimiter",
35            ))
36        }
37    } else {
38        Err(io::Error::from(io::ErrorKind::UnexpectedEof))
39    }
40}
41
42fn maybe_consume_terminator(src: &mut &[u8]) -> io::Result<()> {
43    const TERMINATOR: u8 = b'\t';
44
45    if let Some((b, rest)) = src.split_first() {
46        if *b == TERMINATOR {
47            *src = rest;
48        } else {
49            return Err(io::Error::new(
50                io::ErrorKind::InvalidData,
51                "invalid field terminator",
52            ));
53        }
54    }
55
56    Ok(())
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn test_parse_field() -> io::Result<()> {
65        let mut src = &b"NH:i:1"[..];
66        assert!(matches!(
67            parse_field(&mut src)?,
68            (Tag::ALIGNMENT_HIT_COUNT, Value::Int32(1))
69        ));
70
71        let mut src = &b"NH:i:1\t"[..];
72        assert!(matches!(
73            parse_field(&mut src)?,
74            (Tag::ALIGNMENT_HIT_COUNT, Value::Int32(1))
75        ));
76
77        let mut src = &b""[..];
78        assert!(matches!(
79            parse_field(&mut src),
80            Err(e) if e.kind() == io::ErrorKind::UnexpectedEof
81        ));
82
83        let mut src = &b"NH_i:1"[..];
84        assert!(matches!(
85            parse_field(&mut src),
86            Err(e) if e.kind() == io::ErrorKind::InvalidData
87        ));
88
89        let mut src = &b"NH:i_1"[..];
90        assert!(matches!(
91            parse_field(&mut src),
92            Err(e) if e.kind() == io::ErrorKind::InvalidData
93        ));
94
95        let mut src = &b"NH:i:1\n"[..];
96        assert!(matches!(
97            parse_field(&mut src),
98            Err(e) if e.kind() == io::ErrorKind::InvalidData
99        ));
100
101        Ok(())
102    }
103}