1use parse::UpperName;
2use parse::xml::*;
3use roxmltree::Node;
4use std::collections::BTreeMap;
5use std::ops::Range;
6
7use super::Error;
8
9#[derive(Clone)]
10#[derive(PartialEq)]
11#[repr(transparent)]
12pub struct ClassificationId(usize);
13
14impl Id for ClassificationId {
15 fn zero() -> Self {
16 Self(0)
17 }
18
19 fn succ(&mut self) {
20 self.0 += 1;
21 }
22
23 fn index(&self) -> usize {
24 self.0
25 }
26}
27
28#[derive(Clone)]
29pub struct Classification {
30 pub folders: Vec<ClassificationFolder>,
31 pub has_archive: bool,
32 pub has_description: bool,
33 pub has_national: bool,
34 pub has_national_limit: Option<UpperName>,
35 pub has_national_set: Option<UpperName>,
36 pub has_not_specified: bool,
37 pub has_special: bool,
38 pub has_specific: bool,
39 pub has_state: bool,
40 pub length: usize,
41 pub zero_padded: bool,
42}
43
44#[derive(Clone)]
45pub struct ClassificationFolder {
46 pub can_select: bool,
47 pub length: usize,
48 pub name: UpperName,
49 pub run_pretty: bool,
50 pub zero_select: bool,
51}
52
53struct Folder0 {
54 can_select: bool,
55 length: Parsed<usize>,
56 name: Parsed<UpperName>,
57 run_pretty: bool,
58 zero_select: bool,
59}
60
61pub (crate) struct Limit {
62 pub (crate) range: Range<usize>,
63 pub (crate) limit: UpperName,
64 pub (crate) national: UpperName,
65}
66
67impl Classification {
68 pub (crate) fn parse<'input>(
69 classifications: &mut ParsingMap<UpperName, Classification, ClassificationId>,
70 limits: &mut Vec<Limit>,
71 parent: Node<'input, 'input>,
72 errors: &mut Vec<Error>,
73 ) {
74 let mut file = Once::new("file");
75 let mut folders0 = Vec::new();
76 let mut has_archive = Once::new("archive");
77 let mut has_description = Once::new("description");
78 let mut has_national = Once::new("national");
79 let mut has_national_set = Once::new("national-set");
80 let mut has_not_specified = Once::new("not-specified");
81 let mut has_special = Once::new("special");
82 let mut has_specific = Once::new("specific");
83 let mut has_state = Once::new("state");
84 let mut maximum_length = Once::new("length");
85 let mut name = Once::new("name");
86 let mut title = Once::new("title");
87 let mut zero_padded = Once::new("zero-padded");
88
89 for attribute in parent.attributes() {
90 match attribute.name() {
91 "archive" =>
92 has_archive.boolean_attribute(attribute, errors),
93
94 "description" =>
95 has_description.boolean_attribute(attribute, errors),
96
97 "length" =>
98 maximum_length.length_attribute(1..=10, attribute, errors),
99
100 "name" =>
101 name.upper_name_attribute(attribute, errors),
102
103 "national" =>
104 has_national.boolean_attribute(attribute, errors),
105
106 "national-set" =>
107 has_national_set.boolean_attribute(attribute, errors),
108
109 "not-specified" =>
110 has_not_specified.boolean_attribute(attribute, errors),
111
112 "special" =>
113 has_special.boolean_attribute(attribute, errors),
114
115 "specific" =>
116 has_specific.boolean_attribute(attribute, errors),
117
118 "state" =>
119 has_state.boolean_attribute(attribute, errors),
120
121 "zero-padded" =>
122 zero_padded.boolean_attribute(attribute, errors),
123
124 _ =>
125 unexpected_attribute(attribute, errors),
126 }
127 }
128
129 for element in parent.children() {
130 let Some(name) = element_name(&element, errors) else {
131 continue;
132 };
133
134 match name {
135 "file" =>
136 file.string_element(element, errors),
137
138 "folder" =>
139 Folder0::parse(&mut folders0, element, errors),
140
141 "title" =>
142 title.string_element(element, errors),
143
144 _ =>
145 unexpected_element(element, errors),
146 }
147 }
148
149 let Some(inserted) = classifications.insert(name, &parent, errors) else {
150 return;
151 };
152
153 let Some(maximum_length) = maximum_length.required_attribute(&parent, errors) else {
154 return;
155 };
156
157 let mut failure = false;
158 let mut folders = Vec::new();
159 let mut folder_names: BTreeMap<UpperName, Range<usize>> = BTreeMap::new();
160 let mut minimum_length = 1;
161
162 for folder0 in folders0 {
163 let Some(folder0) = folder0 else {
164 return;
165 };
166
167 if let Some(existing_range) = folder_names.get(&folder0.name.value) {
168 duplicate_attribute(
169 "name",
170 folder0.name.range,
171 existing_range.clone(),
172 errors,
173 );
174
175 failure = true;
176 } else {
177 folder_names.insert(folder0.name.value.clone(), folder0.name.range);
178 }
179
180 if folder0.length.value < minimum_length || folder0.length.value > maximum_length {
181 errors.push(Error::Xml(ParseError::LengthRange {
182 min: minimum_length,
183 max: maximum_length,
184 value: folder0.length.range.into(),
185 }));
186
187 failure = true;
188 } else {
189 minimum_length = folder0.length.value + 1;
190 }
191
192 folders.push(ClassificationFolder {
193 can_select: folder0.can_select,
194 length: folder0.length.value,
195 name: folder0.name.value,
196 run_pretty: folder0.run_pretty,
197 zero_select: folder0.zero_select,
198 });
199 }
200
201 if failure {
202 return;
203 }
204
205 let has_state = has_state.default(false);
206 let has_national_set = if let Some(range) = has_national_set.name_range_if_true() {
207 let national = inserted.name().append("National");
208
209 limits.push(Limit {
210 range,
211 limit: inserted.name().clone(),
212 national: national.clone(),
213 });
214
215 Some(national)
216 } else {
217 None
218 };
219
220 inserted.value(Classification {
221 folders,
222 has_archive: has_archive.default(has_state),
223 has_description: has_description.default(false),
224 has_national: has_national.default(false),
225 has_national_limit: None,
226 has_national_set,
227 has_not_specified: has_not_specified.default(false),
228 has_special: has_special.default(false),
229 has_specific: has_specific.default(false),
230 has_state,
231 length: maximum_length,
232 zero_padded: zero_padded.default(false),
233 });
234 }
235}
236
237impl Folder0 {
238 pub fn parse<'input>(
239 folders: &mut Vec<Option<Self>>,
240 parent: Node<'input, 'input>,
241 errors: &mut Vec<Error>,
242 ) {
243 let mut can_select = Once::new("select");
244 let mut length = Once::new("length");
245 let mut name = Once::new("name");
246 let mut run_pretty = Once::new("pretty");
247 let mut zero_select = Once::new("zero-select");
248
249 for attribute in parent.attributes() {
250 match attribute.name() {
251 "length" =>
252 length.length_attribute(1..=10, attribute, errors),
253
254 "name" =>
255 name.upper_name_attribute(attribute, errors),
256
257 "pretty" =>
258 run_pretty.boolean_attribute(attribute, errors),
259
260 "select" =>
261 can_select.boolean_attribute(attribute, errors),
262
263 "zero-select" =>
264 zero_select.boolean_attribute(attribute, errors),
265
266 _ =>
267 unexpected_attribute(attribute, errors),
268 }
269 }
270
271 for element in parent.children() {
272 unexpected_element(element, errors);
273 }
274
275 if let (
276 Some(length),
277 Some(name),
278 ) = (
279 length.required_parsed_attribute(&parent, errors),
280 name.required_parsed_attribute(&parent, errors),
281 ) {
282 folders.push(Some(Folder0 {
283 can_select: can_select.default(false),
284 length,
285 name,
286 run_pretty: run_pretty.default(false),
287 zero_select: zero_select.default(true),
288 }));
289 } else {
290 folders.push(None);
291 }
292 }
293}