netgauze_ipfix_code_generator/xml_parsers/
sub_registries.rs1use crate::{
17 xml_parsers::xml_common::*, InformationElementSubRegistry, ReasonCodeNestedRegistry,
18 SubRegistryType, ValueNameDescRegistry, Xref,
19};
20
21use regex::Regex;
22use roxmltree::Node;
23
24const MAX_WORDS_NAME: usize = 10;
25const MAX_CHARS_DISPLAY_NAME: usize = 50;
26
27pub trait SubRegistry {
29 fn name(&self) -> &str;
30 fn display_name(&self) -> &str;
31 fn description(&self) -> &str;
32 fn comments(&self) -> &Option<String>;
33 fn parameters(&self) -> &Option<String>;
34 fn xrefs(&self) -> &Vec<Xref>;
35}
36
37impl SubRegistry for ValueNameDescRegistry {
38 fn name(&self) -> &str {
39 &self.name
40 }
41
42 fn display_name(&self) -> &str {
43 &self.display_name
44 }
45
46 fn description(&self) -> &str {
47 &self.description
48 }
49
50 fn comments(&self) -> &Option<String> {
51 &self.comments
52 }
53
54 fn parameters(&self) -> &Option<String> {
55 &self.parameters
56 }
57
58 fn xrefs(&self) -> &Vec<Xref> {
59 &self.xrefs
60 }
61}
62
63impl SubRegistry for ReasonCodeNestedRegistry {
64 fn name(&self) -> &str {
65 &self.name
66 }
67
68 fn display_name(&self) -> &str {
69 &self.display_name
70 }
71
72 fn description(&self) -> &str {
73 &self.description
74 }
75
76 fn comments(&self) -> &Option<String> {
77 &self.comments
78 }
79
80 fn parameters(&self) -> &Option<String> {
81 &self.parameters
82 }
83
84 fn xrefs(&self) -> &Vec<Xref> {
85 &self.xrefs
86 }
87}
88
89pub fn parse_subregistry(
92 node: &Node<'_, '_>,
93 registry_type: SubRegistryType,
94) -> (u16, Vec<InformationElementSubRegistry>) {
95 match registry_type {
96 SubRegistryType::ValueNameDescRegistry => {
97 let (ie_id, reg) = parse_val_name_desc_u8_registry(node);
98 let ie_subreg: Vec<InformationElementSubRegistry> = reg
99 .into_iter()
100 .map(InformationElementSubRegistry::ValueNameDescRegistry)
101 .collect();
102 (ie_id, ie_subreg)
103 }
104 SubRegistryType::ReasonCodeNestedRegistry => {
105 let (ie_id, reg) = parse_reason_code_nested_u8_registry_2bit(node);
106 let ie_subreg: Vec<InformationElementSubRegistry> = reg
107 .into_iter()
108 .map(InformationElementSubRegistry::ReasonCodeNestedRegistry)
109 .collect();
110 (ie_id, ie_subreg)
111 }
112 }
113}
114
115pub fn parse_val_name_desc_u8_registry(node: &Node<'_, '_>) -> (u16, Vec<ValueNameDescRegistry>) {
120 let mut ret = Vec::new();
121
122 let children = node
123 .children()
124 .filter(|x| x.tag_name() == (IANA_NAMESPACE, "record").into())
125 .collect::<Vec<_>>();
126
127 let title = get_string_child(node, (IANA_NAMESPACE, "title").into()).unwrap_or_default();
128
129 let ie_id_regex = Regex::new(r"Value (\d+)").unwrap();
130 let ie_id = ie_id_regex
131 .captures(&title)
132 .and_then(|captures| captures.get(1))
133 .and_then(|capture| capture.as_str().parse().ok())
134 .unwrap_or(0);
135
136 for child in &children {
137 let value = get_string_child(child, (IANA_NAMESPACE, "value").into()).map(|x| {
138 if let Some(hex_value) = x.strip_prefix("0x") {
139 u8::from_str_radix(hex_value, 16)
140 } else if let Some(bin_value) = x.strip_prefix("0b") {
141 u8::from_str_radix(bin_value, 2)
142 } else if let Some(bin_value) = x.strip_suffix('b') {
143 u8::from_str_radix(bin_value, 2)
144 } else {
145 x.parse::<u8>()
146 }
147 });
148
149 let value = match value {
152 Some(_) => value,
153 None => get_string_child(child, (IANA_NAMESPACE, "id").into()).map(|x| {
154 if let Some(hex_value) = x.strip_prefix("0x") {
155 u8::from_str_radix(hex_value, 16)
156 } else if let Some(bin_value) = x.strip_prefix("0b") {
157 u8::from_str_radix(bin_value, 2)
158 } else if let Some(bin_value) = x.strip_suffix('b') {
159 u8::from_str_radix(bin_value, 2)
160 } else {
161 x.parse::<u8>()
162 }
163 }),
164 };
165
166 let name_parsed = get_string_child(child, (IANA_NAMESPACE, "name").into());
167
168 if Some(true)
170 == name_parsed
171 .as_ref()
172 .map(|x| x.as_str() == UNASSIGNED || x.contains(EXPERIMENTATION))
173 {
174 continue;
175 }
176
177 let description_parsed = parse_simple_description_string(child);
178 if Some(true)
179 == description_parsed
180 .as_ref()
181 .map(|x| x.as_str() == UNASSIGNED || x.contains(EXPERIMENTATION))
182 {
183 continue;
184 }
185
186 let mut name: String;
192 let mut display_name: String;
193 let description: String;
194 if let Some(Ok(value)) = value {
195 if value == u8::MAX {
196 continue;
198 }
199
200 if let Some(name_parsed) = name_parsed {
201 display_name = name_parsed.clone();
202
203 (_, name) = xml_string_to_enum_type(&name_parsed);
204 if let Some(desc_parsed) = description_parsed {
205 description = desc_parsed;
206 } else {
207 description = name_parsed;
208 }
209 } else if let Some(mut desc_parsed) = description_parsed {
210 description = desc_parsed.clone();
211
212 let desc_words_amount: usize;
213 (desc_words_amount, desc_parsed) = xml_string_to_enum_type(&desc_parsed);
214
215 if desc_words_amount < MAX_WORDS_NAME {
216 display_name = desc_parsed.clone();
217 name = desc_parsed;
218 } else {
219 display_name = format!("Value{value}");
220 name = format!("Value{value}");
221 }
222
223 if description.len() < MAX_CHARS_DISPLAY_NAME {
224 display_name = description.clone();
225 }
226 } else {
227 log::info!("Skipping sub-registry: missing both name and description!");
228 continue;
229 }
230
231 if name == *RESERVED || name == *PRIVATE {
233 name = format!("{name}{value}");
234 }
235
236 let comments = get_string_child(child, (IANA_NAMESPACE, "comments").into());
237 let parameters = get_string_child(child, (IANA_NAMESPACE, "parameters").into());
238 let xrefs = parse_xref(child);
239
240 ret.push(ValueNameDescRegistry {
241 value,
242 name,
243 display_name,
244 description,
245 comments,
246 parameters,
247 xrefs,
248 });
249 }
250 }
251
252 (ie_id, ret)
253}
254
255pub fn parse_reason_code_nested_u8_registry_2bit(
258 node: &Node<'_, '_>,
259) -> (u16, Vec<ReasonCodeNestedRegistry>) {
260 let (ie_id, subreg) = parse_val_name_desc_u8_registry(node);
261
262 let ret: Vec<ReasonCodeNestedRegistry> = subreg
263 .iter()
264 .map(|subreg| {
265 let val_bin_str = format!("{:02b}", subreg.value);
266 let reason_code_pattern = format!(r".*-{val_bin_str}b");
267 let reason_code_reg_pattern = Regex::new(&reason_code_pattern).unwrap();
268 let reason_code_node = find_node_by_regex(node, &reason_code_reg_pattern).unwrap();
269 ReasonCodeNestedRegistry {
270 value: subreg.value << 6,
271 name: SubRegistry::name(subreg).to_string(),
272 display_name: SubRegistry::display_name(subreg).to_string(),
273 description: SubRegistry::description(subreg).to_string(),
274 comments: SubRegistry::comments(subreg).to_owned(),
275 parameters: SubRegistry::parameters(subreg).to_owned(),
276 xrefs: SubRegistry::xrefs(subreg).to_owned(),
277 reason_code_reg: {
278 let (_, reg) = parse_val_name_desc_u8_registry(&reason_code_node);
279 let reg: Vec<InformationElementSubRegistry> = reg
280 .into_iter()
281 .map(InformationElementSubRegistry::ValueNameDescRegistry)
282 .collect();
283 reg
284 },
285 }
286 })
287 .collect();
288
289 (ie_id, ret)
290}