const_oid/
parser.rs
1use crate::{encoder::Encoder, Arc, ObjectIdentifier};
4
5pub(crate) struct Parser {
9 current_arc: Arc,
11
12 encoder: Encoder,
14}
15
16impl Parser {
17 pub(crate) const fn parse(s: &str) -> Self {
19 let bytes = s.as_bytes();
20 const_assert!(!bytes.is_empty(), "OID string is empty");
21 const_assert!(
22 matches!(bytes[0], b'0'..=b'9'),
23 "OID must start with a digit"
24 );
25
26 let current_arc = 0;
27 let encoder = Encoder::new();
28 Self {
29 current_arc,
30 encoder,
31 }
32 .parse_bytes(bytes)
33 }
34
35 pub(crate) const fn finish(self) -> ObjectIdentifier {
37 self.encoder.finish()
38 }
39
40 const fn parse_bytes(mut self, bytes: &[u8]) -> Self {
42 match bytes {
43 [] => {
44 self.encoder = self.encoder.encode(self.current_arc);
45 self
46 }
47 [byte @ b'0'..=b'9', remaining @ ..] => {
48 let digit = byte.saturating_sub(b'0');
49 self.current_arc = self.current_arc * 10 + digit as Arc;
50 self.parse_bytes(remaining)
51 }
52 [b'.', remaining @ ..] => {
53 const_assert!(!remaining.is_empty(), "invalid trailing '.' in OID");
54 self.encoder = self.encoder.encode(self.current_arc);
55 self.current_arc = 0;
56 self.parse_bytes(remaining)
57 }
58 [byte, ..] => {
59 const_assert!(
60 matches!(byte, b'0'..=b'9' | b'.'),
61 "invalid character in OID"
62 );
63
64 self
67 }
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::Parser;
75
76 #[test]
77 fn parse() {
78 let oid = Parser::parse("1.23.456").finish();
79 assert_eq!(oid, "1.23.456".parse().unwrap());
80 }
81
82 #[test]
83 #[should_panic]
84 fn reject_empty_string() {
85 Parser::parse("");
86 }
87
88 #[test]
89 #[should_panic]
90 fn reject_non_digits() {
91 Parser::parse("X");
92 }
93
94 #[test]
95 #[should_panic]
96 fn reject_trailing_dot() {
97 Parser::parse("1.23.");
98 }
99}