1use std::collections::HashMap;
4
5use lazy_static::lazy_static;
6
7use crate::error::Error;
8use crate::tokenizer::Token;
9
10use crate::parser::utils::{expect_one_of_tokens, expect_token, expect_tokens};
11
12use super::structs::oid::{OIDComponent, ObjectIdentifier};
13
14lazy_static! {
15 pub(crate) static ref WELL_KNOWN_OID_NAMES: HashMap<&'static str, u32> = {
16 let mut m = HashMap::new();
17 m.insert("iso", 1);
18 m.insert("itu-t", 0);
19 m.insert("ccitt", 0);
20 m.insert("joint-iso-itu-t", 2);
21 m.insert("joint-iso-ccitt", 2);
22 m.insert("identified-organization", 4);
24 m.insert("network-operator", 3);
25 m.insert("administration", 2);
26 m.insert("question", 1);
27 m
28 };
29}
30
31fn parse_named_oid_component(tokens: &[Token]) -> Result<(OIDComponent, usize), Error> {
35 if !expect_token(tokens, Token::is_value_reference)? {
36 return Err(unexpected_token!("'IDENTIFIER'", tokens[0]));
37 }
38 let name_token = &tokens[0];
39 let name = &name_token.text;
40 let (number, consumed) = match expect_tokens(
41 &tokens[1..],
42 &[
43 &[Token::is_round_begin],
44 &[Token::is_numeric],
45 &[Token::is_round_end],
46 ],
47 ) {
48 Ok(success) => {
49 if success {
50 let number_token = &tokens[2];
51 let number = number_token
52 .text
53 .parse::<u32>()
54 .map_err(|_| invalid_token!(number_token))?;
55 (number, 4)
56 } else {
57 let number = WELL_KNOWN_OID_NAMES.get(name.as_str());
58 if number.is_none() {
59 return Err(unknown_oid_name!(name_token));
60 }
61 (*number.unwrap(), 1)
62 }
63 }
64 Err(_) => {
65 let number = WELL_KNOWN_OID_NAMES.get(name.as_str());
66 if number.is_none() {
67 return Err(unknown_oid_name!(name_token));
68 }
69 (*number.unwrap(), 1)
70 }
71 };
72
73 Ok((OIDComponent::new(Some(name.clone()), number), consumed))
74}
75
76fn parse_oid_component(tokens: &[Token]) -> Result<(OIDComponent, usize), Error> {
80 let consumed = 0;
81
82 if expect_one_of_tokens(
83 &tokens[consumed..],
84 &[Token::is_identifier, Token::is_numeric],
85 )? {
86 let first = &tokens[0];
87 if first.is_identifier() {
88 parse_named_oid_component(tokens)
89 } else {
90 let number = first
91 .text
92 .parse::<u32>()
93 .map_err(|_| invalid_token!(first))?;
94 Ok((OIDComponent::new(None, number), 1))
95 }
96 } else {
97 Err(unexpected_token!(
98 "Expected 'identifier' or 'number'",
99 tokens[0]
100 ))
101 }
102}
103
104pub(crate) fn parse_object_identifier(
108 tokens: &[Token],
109) -> Result<(ObjectIdentifier, usize), Error> {
110 let mut consumed = 0;
111
112 if !expect_token(&tokens[consumed..], Token::is_curly_begin)? {
113 return Err(unexpected_token!("{", tokens[consumed]));
114 }
115 consumed += 1;
116
117 let mut components = vec![];
118 while !expect_token(&tokens[consumed..], Token::is_curly_end)? {
119 let (component, component_consumed) = parse_oid_component(&tokens[consumed..])?;
120 components.push(component);
121 consumed += component_consumed;
122 }
123 consumed += 1;
124
125 Ok((ObjectIdentifier::new(components), consumed))
128}
129
130#[cfg(test)]
131mod tests {
132
133 use super::*;
134 use crate::tokenizer::tokenize;
135
136 struct OIDTestCase<'testcase> {
137 input: &'testcase str,
138 success: bool,
139 consumed: usize,
140 parse_component_only: bool,
141 components_count: usize,
142 }
143
144 #[test]
145 fn object_identifier_cases() {
146 let test_cases = vec![
147 OIDTestCase {
148 input: " iso ",
149 success: true,
150 consumed: 1,
151 parse_component_only: true,
152 components_count: 0,
153 },
154 OIDTestCase {
155 input: " foo ",
156 success: false,
157 consumed: 1,
158 parse_component_only: true,
159 components_count: 0,
160 },
161 OIDTestCase {
162 input: " something(3) ",
163 success: true,
164 consumed: 4,
165 parse_component_only: true,
166 components_count: 0,
167 },
168 OIDTestCase {
169 input: " something(-3) ",
170 success: false,
171 consumed: 4,
172 parse_component_only: true,
173 components_count: 0,
174 },
175 OIDTestCase {
176 input: " iso() ",
177 success: true,
178 consumed: 1,
179 parse_component_only: true,
180 components_count: 0,
181 },
182 OIDTestCase {
183 input: "{ iso() }",
184 success: false,
185 consumed: 4,
186 parse_component_only: false,
187 components_count: 0,
188 },
189 OIDTestCase {
190 input: "{ iso(1 }",
191 success: false,
192 consumed: 4,
193 parse_component_only: false,
194 components_count: 0,
195 },
196 OIDTestCase {
197 input: " { iso something(3) }",
198 success: true,
199 consumed: 7,
200 parse_component_only: false,
201 components_count: 2,
202 },
203 OIDTestCase {
204 input: "{ iso }",
205 success: true,
206 consumed: 3,
207 parse_component_only: false,
208 components_count: 1,
209 },
210 ];
211
212 for tc in test_cases {
213 let reader = std::io::BufReader::new(std::io::Cursor::new(tc.input));
214 let tokens = tokenize(reader);
215 assert!(tokens.is_ok());
216
217 let tokens = tokens.unwrap();
218 if tc.parse_component_only {
219 let oidcomp = parse_oid_component(&tokens);
220 assert_eq!(oidcomp.is_ok(), tc.success, "{:#?}", tc.input);
221 if tc.success {
222 let (_oidcomp, consumed) = oidcomp.unwrap();
223 assert_eq!(consumed, tc.consumed, "{:#?}", tc.input);
224 }
225 } else {
226 let oid = parse_object_identifier(&tokens);
227 assert_eq!(oid.is_ok(), tc.success, "{:#?}", tc.input);
228 if tc.success {
229 let (oid, consumed) = oid.unwrap();
230 assert_eq!(oid.len(), tc.components_count);
231 assert_eq!(consumed, tc.consumed);
232 }
233 }
234 }
235 }
236}