/rust/registry/src/index.crates.io-1949cf8c6b5b557f/const-oid-0.10.2/src/parser.rs
Line | Count | Source |
1 | | //! OID string parser with `const` support. |
2 | | |
3 | | use crate::{Arc, Error, ObjectIdentifier, Result, encoder::Encoder}; |
4 | | |
5 | | /// Const-friendly OID string parser. |
6 | | /// |
7 | | /// Parses an OID from the dotted string representation. |
8 | | #[derive(Debug)] |
9 | | pub(crate) struct Parser { |
10 | | /// Current arc in progress |
11 | | current_arc: Option<Arc>, |
12 | | |
13 | | /// BER/DER encoder |
14 | | encoder: Encoder<{ ObjectIdentifier::MAX_SIZE }>, |
15 | | } |
16 | | |
17 | | impl Parser { |
18 | | /// Parse an OID from a dot-delimited string e.g. `1.2.840.113549.1.1.1` |
19 | 0 | pub(crate) const fn parse(s: &str) -> Result<Self> { |
20 | 0 | let bytes = s.as_bytes(); |
21 | | |
22 | 0 | if bytes.is_empty() { |
23 | 0 | return Err(Error::Empty); |
24 | 0 | } |
25 | | |
26 | 0 | match bytes[0] { |
27 | 0 | b'0'..=b'9' => Self { |
28 | 0 | current_arc: None, |
29 | 0 | encoder: Encoder::new(), |
30 | 0 | } |
31 | 0 | .parse_bytes(bytes), |
32 | 0 | actual => Err(Error::DigitExpected { actual }), |
33 | | } |
34 | 0 | } |
35 | | |
36 | | /// Finish parsing, returning the result |
37 | 0 | pub(crate) const fn finish(self) -> Result<ObjectIdentifier> { |
38 | 0 | self.encoder.finish() |
39 | 0 | } |
40 | | |
41 | | /// Parse the remaining bytes |
42 | 0 | const fn parse_bytes(mut self, bytes: &[u8]) -> Result<Self> { |
43 | 0 | match bytes { |
44 | | // TODO(tarcieri): use `?` when stable in `const fn` |
45 | 0 | [] => match self.current_arc { |
46 | 0 | Some(arc) => match self.encoder.arc(arc) { |
47 | 0 | Ok(encoder) => { |
48 | 0 | self.encoder = encoder; |
49 | 0 | Ok(self) |
50 | | } |
51 | 0 | Err(err) => Err(err), |
52 | | }, |
53 | 0 | None => Err(Error::TrailingDot), |
54 | | }, |
55 | 0 | [byte @ b'0'..=b'9', remaining @ ..] => { |
56 | 0 | let digit = byte.saturating_sub(b'0'); |
57 | 0 | let arc = match self.current_arc { |
58 | 0 | Some(arc) => arc, |
59 | 0 | None => 0, |
60 | | }; |
61 | | |
62 | | // TODO(tarcieri): use `and_then` when const traits are stable |
63 | 0 | self.current_arc = match arc.checked_mul(10) { |
64 | 0 | Some(arc) => match arc.checked_add(digit as Arc) { |
65 | 0 | None => return Err(Error::ArcTooBig), |
66 | 0 | Some(arc) => Some(arc), |
67 | | }, |
68 | 0 | None => return Err(Error::ArcTooBig), |
69 | | }; |
70 | 0 | self.parse_bytes(remaining) |
71 | | } |
72 | 0 | [b'.', remaining @ ..] => { |
73 | 0 | match self.current_arc { |
74 | 0 | Some(arc) => { |
75 | 0 | if remaining.is_empty() { |
76 | 0 | return Err(Error::TrailingDot); |
77 | 0 | } |
78 | | |
79 | | // TODO(tarcieri): use `?` when stable in `const fn` |
80 | 0 | match self.encoder.arc(arc) { |
81 | 0 | Ok(encoder) => { |
82 | 0 | self.encoder = encoder; |
83 | 0 | self.current_arc = None; |
84 | 0 | self.parse_bytes(remaining) |
85 | | } |
86 | 0 | Err(err) => Err(err), |
87 | | } |
88 | | } |
89 | 0 | None => Err(Error::RepeatedDot), |
90 | | } |
91 | | } |
92 | 0 | [byte, ..] => Err(Error::DigitExpected { actual: *byte }), |
93 | | } |
94 | 0 | } |
95 | | } |
96 | | |
97 | | #[cfg(test)] |
98 | | #[allow(clippy::unwrap_used)] |
99 | | mod tests { |
100 | | use super::Parser; |
101 | | use crate::Error; |
102 | | |
103 | | #[test] |
104 | | fn parse() { |
105 | | let oid = Parser::parse("1.23.456").unwrap().finish().unwrap(); |
106 | | assert_eq!(oid, "1.23.456".parse().unwrap()); |
107 | | } |
108 | | |
109 | | #[test] |
110 | | fn reject_empty_string() { |
111 | | assert_eq!(Parser::parse("").err().unwrap(), Error::Empty); |
112 | | } |
113 | | |
114 | | #[test] |
115 | | fn reject_non_digits() { |
116 | | assert_eq!( |
117 | | Parser::parse("X").err().unwrap(), |
118 | | Error::DigitExpected { actual: b'X' } |
119 | | ); |
120 | | |
121 | | assert_eq!( |
122 | | Parser::parse("1.2.X").err().unwrap(), |
123 | | Error::DigitExpected { actual: b'X' } |
124 | | ); |
125 | | } |
126 | | |
127 | | #[test] |
128 | | fn reject_trailing_dot() { |
129 | | assert_eq!(Parser::parse("1.23.").err().unwrap(), Error::TrailingDot); |
130 | | } |
131 | | } |