const_oid/
parser.rs
1use crate::{encoder::Encoder, Arc, Error, ObjectIdentifier, Result};
4
5#[derive(Debug)]
9pub(crate) struct Parser {
10 current_arc: Arc,
12
13 encoder: Encoder,
15}
16
17impl Parser {
18 pub(crate) const fn parse(s: &str) -> Result<Self> {
20 let bytes = s.as_bytes();
21
22 if bytes.is_empty() {
23 return Err(Error::Empty);
24 }
25
26 match bytes[0] {
27 b'0'..=b'9' => Self {
28 current_arc: 0,
29 encoder: Encoder::new(),
30 }
31 .parse_bytes(bytes),
32 actual => Err(Error::DigitExpected { actual }),
33 }
34 }
35
36 pub(crate) const fn finish(self) -> Result<ObjectIdentifier> {
38 self.encoder.finish()
39 }
40
41 const fn parse_bytes(mut self, bytes: &[u8]) -> Result<Self> {
43 match bytes {
44 [] => match self.encoder.arc(self.current_arc) {
46 Ok(encoder) => {
47 self.encoder = encoder;
48 Ok(self)
49 }
50 Err(err) => Err(err),
51 },
52 #[allow(clippy::integer_arithmetic)]
54 [byte @ b'0'..=b'9', remaining @ ..] => {
55 let digit = byte.saturating_sub(b'0');
56 self.current_arc = self.current_arc * 10 + digit as Arc;
57 self.parse_bytes(remaining)
58 }
59 [b'.', remaining @ ..] => {
60 if remaining.is_empty() {
61 return Err(Error::TrailingDot);
62 }
63
64 match self.encoder.arc(self.current_arc) {
66 Ok(encoder) => {
67 self.encoder = encoder;
68 self.current_arc = 0;
69 self.parse_bytes(remaining)
70 }
71 Err(err) => Err(err),
72 }
73 }
74 [byte, ..] => Err(Error::DigitExpected { actual: *byte }),
75 }
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::Parser;
82 use crate::Error;
83
84 #[test]
85 fn parse() {
86 let oid = Parser::parse("1.23.456").unwrap().finish().unwrap();
87 assert_eq!(oid, "1.23.456".parse().unwrap());
88 }
89
90 #[test]
91 fn reject_empty_string() {
92 assert_eq!(Parser::parse("").err().unwrap(), Error::Empty);
93 }
94
95 #[test]
96 fn reject_non_digits() {
97 assert_eq!(
98 Parser::parse("X").err().unwrap(),
99 Error::DigitExpected { actual: b'X' }
100 );
101
102 assert_eq!(
103 Parser::parse("1.2.X").err().unwrap(),
104 Error::DigitExpected { actual: b'X' }
105 );
106 }
107
108 #[test]
109 fn reject_trailing_dot() {
110 assert_eq!(Parser::parse("1.23.").err().unwrap(), Error::TrailingDot);
111 }
112}