noodles_sam/header/parser/record/value/map/
program.rs1use std::{error, fmt};
2
3use bstr::{BStr, BString};
4
5use super::field::{consume_delimiter, consume_separator, parse_tag, parse_value, value};
6use crate::header::{
7 parser::Context,
8 record::value::{
9 Map,
10 map::{
11 self, OtherFields, Program,
12 program::{Tag, tag},
13 tag::Other,
14 },
15 },
16};
17
18#[derive(Clone, Debug, Eq, PartialEq)]
20pub enum ParseError {
21 InvalidField(super::field::ParseError),
22 InvalidTag(super::field::tag::ParseError),
23 InvalidValue(value::ParseError),
24 MissingId,
25 InvalidId(value::ParseError),
26 InvalidOther(Other<tag::Standard>, value::ParseError),
27 DuplicateTag(Tag),
28}
29
30impl error::Error for ParseError {
31 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
32 match self {
33 Self::InvalidField(e) => Some(e),
34 Self::InvalidTag(e) => Some(e),
35 Self::InvalidId(e) => Some(e),
36 Self::InvalidOther(_, e) => Some(e),
37 _ => None,
38 }
39 }
40}
41
42impl fmt::Display for ParseError {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 Self::InvalidField(_) => write!(f, "invalid field"),
46 Self::InvalidTag(_) => write!(f, "invalid tag"),
47 Self::InvalidValue(_) => write!(f, "invalid value"),
48 Self::MissingId => write!(f, "missing ID field"),
49 Self::InvalidId(_) => write!(f, "invalid ID"),
50 Self::InvalidOther(tag, _) => write!(f, "invalid other ({tag})"),
51 Self::DuplicateTag(tag) => write!(f, "duplicate tag: {tag}"),
52 }
53 }
54}
55
56pub(crate) fn parse_program(
57 src: &mut &[u8],
58 ctx: &Context,
59) -> Result<(BString, Map<Program>), ParseError> {
60 let mut id = None;
61
62 let mut other_fields = OtherFields::new();
63
64 while !src.is_empty() {
65 consume_delimiter(src).map_err(ParseError::InvalidField)?;
66 let tag = parse_tag(src).map_err(ParseError::InvalidTag)?;
67 consume_separator(src).map_err(ParseError::InvalidField)?;
68
69 match tag {
70 tag::ID => parse_id(src).and_then(|v| try_replace(&mut id, ctx, tag::ID, v))?,
71 Tag::Other(t) => parse_other(src, t)
72 .and_then(|value| try_insert(&mut other_fields, ctx, t, value))?,
73 }
74 }
75
76 let id = id.ok_or(ParseError::MissingId)?;
77
78 Ok((
79 id.into(),
80 Map {
81 inner: Program,
82 other_fields,
83 },
84 ))
85}
86
87fn parse_id<'a>(src: &mut &'a [u8]) -> Result<&'a BStr, ParseError> {
88 parse_value(src).map_err(ParseError::InvalidId)
89}
90
91fn parse_other<'a>(src: &mut &'a [u8], tag: Other<tag::Standard>) -> Result<&'a BStr, ParseError> {
92 parse_value(src).map_err(|e| ParseError::InvalidOther(tag, e))
93}
94
95fn try_replace<T>(
96 option: &mut Option<T>,
97 ctx: &Context,
98 tag: Tag,
99 value: T,
100) -> Result<(), ParseError> {
101 if option.replace(value).is_some() && !ctx.allow_duplicate_tags() {
102 Err(ParseError::DuplicateTag(tag))
103 } else {
104 Ok(())
105 }
106}
107
108fn try_insert<V>(
109 other_fields: &mut OtherFields<tag::Standard>,
110 ctx: &Context,
111 tag: map::tag::Other<tag::Standard>,
112 value: V,
113) -> Result<(), ParseError>
114where
115 V: Into<BString>,
116{
117 if other_fields.insert(tag, value.into()).is_some() && !ctx.allow_duplicate_tags() {
118 Err(ParseError::DuplicateTag(Tag::Other(tag)))
119 } else {
120 Ok(())
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_parse_program() {
130 let mut src = &b"\tID:pg0"[..];
131 let ctx = Context::default();
132 let actual = parse_program(&mut src, &ctx);
133 let expected = (BString::from("pg0"), Map::<Program>::default());
134 assert_eq!(actual, Ok(expected));
135 }
136
137 #[test]
138 fn test_parse_program_with_missing_id() {
139 let mut src = &b"\tPN:pg0"[..];
140 let ctx = Context::default();
141 assert_eq!(parse_program(&mut src, &ctx), Err(ParseError::MissingId));
142 }
143}