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