1extern crate sxd_document;
2
3use std::collections::HashMap;
4
5use sxd_document::{
6 parser,
7 Package,
8};
9use sxd_document::dom::{
10 Document,
11 ChildOfRoot,
12 Element,
13 ChildOfElement,
14};
15
16#[derive(Debug)]
17pub struct Program {
18 pub groups: Vec<StatementBody>
19}
20
21#[derive(PartialEq, Debug)]
22pub struct StatementBody {
23 pub blocks: Vec<Block>
24}
25
26#[derive(PartialEq, Debug)]
27pub struct Block {
28 pub block_type: String,
29 pub id: String,
30 pub fields: HashMap<String, FieldValue>,
31 pub statements: HashMap<String, StatementBody>,
32}
33
34#[derive(PartialEq, Debug)]
35pub enum FieldValue {
36 SimpleField(String),
37 ExpressionField(Block),
38}
39
40impl Program {
41 pub fn new() -> Self {
42 Self {
43 groups: Vec::new()
44 }
45 }
46}
47
48impl StatementBody {
49 fn new(first_block: Option<Element>) -> Self {
50 let mut blocks = Vec::new();
51 if let Some(el) = first_block {
52 let mut block_el: Element;
54 block_el = el;
55 loop {
56 blocks.push(Block::new(block_el));
57 if let Some(next_block) = get_next_block_element(&block_el) {
58 block_el = next_block;
59 } else {
60 break;
61 }
62 }
63 }
64 Self {
65 blocks
66 }
67 }
68}
69
70impl Block {
71 fn new(block_el: Element) -> Self {
72 let mut block = Self {
73 block_type: "".to_string(),
74 id: "".to_string(),
75 fields: HashMap::new(),
76 statements: HashMap::new()
77 };
78
79 for attribute in block_el.attributes().iter() {
80 let name = attribute.name().local_part();
81 let value = attribute.value().to_string();
82 match name {
83 "type" => { block.block_type = value; },
84 "id" => { block.id = value; },
85 _ => {}
86 }
87 }
88
89 for child in block_el.children().iter() {
90 if let &ChildOfElement::Element(child_el) = child {
91 let child_name = child_el.name().local_part();
92 match child_name {
93 "statement" => {
94 let statement_el = child_el;
95 let statement_name = get_attribute(statement_el, "name").unwrap();
96 let statement_body = StatementBody::new(get_first_child_element(statement_el));
97 block.statements.insert(statement_name, statement_body);
98 },
99 "field" => {
100 let field_el = child_el;
101 let field_name = get_attribute(field_el, "name").unwrap();
102 let field_value = FieldValue::new(field_el);
103 block.fields.insert(field_name, field_value);
104 },
105 _ => {}
106 }
107 }
108 }
109
110 block
111 }
112}
113
114impl FieldValue {
115 fn new(field_el: Element) -> Self {
116 for child in field_el.children().iter() {
117 match child {
118 &ChildOfElement::Text(text_node) => {
119 let value = text_node.text().to_string();
120 return FieldValue::SimpleField(value);
121 },
122 _ => panic!("TODO: Implement expression fields")
123 }
124 }
125 panic!("Expected child nodes for field");
126 }
127}
128
129pub fn program_from_xml(xml: &str) -> Program {
132 let mut program = Program::new();
133
134 let package: Package = parser::parse(xml).expect("Failed to parse XML!");
135 let document: Document = package.as_document();
136
137 let xml_element = get_xml_element(document).expect("Failed to find XML element!");
138
139 for child in xml_element.children().iter() {
140 if let &ChildOfElement::Element(el) = child {
141 let element_name = el.name().local_part();
142 match element_name {
143 "block" => {
144 program.groups.push(StatementBody::new(Some(el)));
145 },
146 _ => {}
148 }
149 }
150 }
151
152 program
153}
154
155fn get_next_block_element<'b>(block_el: &Element<'b>) -> Option<Element<'b>> {
156 let next_el: Option<Element> = block_el.children()
157 .iter()
158 .filter_map(|child| {
159 if let &ChildOfElement::Element(el) = child {
160 if el.name().local_part() == "next" {
161 return Some(el);
162 }
163 }
164 None
165 })
166 .next();
167
168 if let Some(next_el) = next_el {
169 let next_block_el: Option<Element> = next_el.children()
170 .iter()
171 .filter_map(|&child| {
172 if let ChildOfElement::Element(el) = child {
173 if el.name().local_part() == "block" {
174 return Some(el);
175 }
176 }
177 None
178 })
179 .next();
180 return next_block_el;
181 }
182
183 None
184}
185
186fn get_xml_element(document: Document) -> Option<Element> {
189 document.root()
190 .children()
191 .iter()
192 .filter_map(|child| {
193 if let &ChildOfRoot::Element(el) = child {
194 if el.name().local_part() == "xml" {
195 return Some(el);
196 }
197 }
198 None
199 })
200 .next()
201}
202
203fn get_first_child_element(element: Element) -> Option<Element> {
204 element.children()
205 .iter()
206 .filter_map(|child| {
207 if let &ChildOfElement::Element(el) = child {
208 return Some(el);
209 }
210 None
211 })
212 .next()
213}
214
215fn get_attribute(element: Element, attribute_name: &str) -> Option<String> {
216 element.attributes()
217 .iter()
218 .filter_map(|attribute| {
219 let name = attribute.name().local_part();
220 if name == attribute_name {
221 let value = attribute.value().to_string();
222 return Some(value);
223 }
224 None
225 })
226 .next()
227}
228
229
230#[cfg(test)]
231mod test {
232 use super::*;
233
234 fn get_fragment_root(package: &Package) -> Option<Element> {
235 package.as_document()
236 .root()
237 .children()
238 .iter()
239 .filter_map(|child| {
240 if let &ChildOfRoot::Element(el) = child {
241 return Some(el);
242 }
243 None
244 })
245 .next()
246 }
247
248 #[test]
249 fn test_new_block() {
250 let xml: &str = r#"
251 <block type="inner_loop" id="]Lb|t?wfd#;s)[llJx8Y">
252 <field name="COUNT">3</field>
253 <statement name="BODY">
254 </statement>
255 </block>
256 "#;
257 let fragment: Package = parser::parse(xml).expect("Failed to parse XML!");
258 let root_element = get_fragment_root(&fragment).unwrap();
259
260 let block = Block::new(root_element);
261 assert_eq!(block.block_type, "inner_loop");
262 assert_eq!(block.id, "]Lb|t?wfd#;s)[llJx8Y");
263 let count_field = block.fields.get("COUNT");
264 assert!(count_field.is_some());
265 assert_eq!(count_field.unwrap(), &FieldValue::SimpleField("3".to_string()));
266 }
267
268 #[test]
269 fn test_get_next_block_element() {
270 let xml: &str = r#"
271 <block type="led_on" id="^3xb.m4E9i0;3$R10(=5">
272 <field name="TIME">300</field>
273 <next>
274 <block type="led_off" id="HX4*sB9=gbJtq$Y{ke6b">
275 <field name="TIME">100</field>
276 </block>
277 </next>
278 </block>
279 "#;
280 let fragment: Package = parser::parse(xml).expect("Failed to parse XML!");
281 let root_element = get_fragment_root(&fragment).unwrap();
282
283 let next_block = get_next_block_element(&root_element);
284 assert!(next_block.is_some());
285 let next_block_unwrapped = next_block.unwrap();
286 assert_eq!(get_attribute(next_block_unwrapped, "type"), Some("led_off".to_string()));
287 assert_eq!(get_attribute(next_block_unwrapped, "id"), Some("HX4*sB9=gbJtq$Y{ke6b".to_string()));
288 }
289
290 #[test]
291 fn test_program_from_xml_advanced() {
292 let xml: &str = r#"
293 <xml xmlns="http://www.w3.org/1999/xhtml">
294 <variables></variables>
295 <block type="main_loop" id="[.)/fqUYv92(mzb{?:~u" deletable="false" movable="false" x="50" y="50">
296 <statement name="BODY">
297 <block type="inner_loop" id="]Lb|t?wfd#;s)[llJx8Y">
298 <field name="COUNT">3</field>
299 <statement name="BODY">
300 <block type="led_on" id="^3xb.m4E9i0;3$R10(=5">
301 <field name="TIME">300</field>
302 <next>
303 <block type="led_off" id="HX4*sB9=gbJtq$Y{ke6b">
304 <field name="TIME">100</field>
305 </block>
306 </next>
307 </block>
308 </statement>
309 <next>
310 <block type="led_on" id="kB~f~7W`wkGa0i4z3mHw">
311 <field name="TIME">100</field>
312 <next>
313 <block type="led_off" id="$fdlZB)btzA8YtB/!xz`">
314 <field name="TIME">100</field>
315 </block>
316 </next>
317 </block>
318 </next>
319 </block>
320 </statement>
321 </block>
322 </xml>
323 "#;
324
325 let program: Program = program_from_xml(xml);
326 assert_eq!(program.groups.len(), 1);
327
328 let group = program.groups.get(0).unwrap();
329 assert_eq!(group.blocks.len(), 1);
330
331 let main_loop_block = group.blocks.get(0).unwrap();
332 assert_eq!(main_loop_block.block_type, "main_loop");
333 assert_eq!(main_loop_block.id, "[.)/fqUYv92(mzb{?:~u");
334
335 let main_loop_statements = &main_loop_block.statements;
336 assert_eq!(main_loop_statements.len(), 1);
337 assert!(main_loop_statements.contains_key("BODY"));
338
339 let main_loop_body = main_loop_statements.get("BODY");
340 let main_loop_body_statement = main_loop_body.as_ref().unwrap();
341 assert_eq!(main_loop_body_statement.blocks.len(), 3);
342
343 let inner_loop_block = main_loop_body_statement.blocks.get(0).unwrap();
344 assert_eq!(inner_loop_block.block_type, "inner_loop");
345 assert_eq!(inner_loop_block.id, "]Lb|t?wfd#;s)[llJx8Y");
346 assert_eq!(inner_loop_block.fields.get("COUNT"), Some(&FieldValue::SimpleField("3".to_string())));
347
348 let inner_loop_statement_maybe = inner_loop_block.statements.get("BODY");
349 assert!(inner_loop_statement_maybe.is_some());
350 let inner_loop_statement = inner_loop_statement_maybe.unwrap();
351 assert_eq!(inner_loop_statement.blocks.len(), 2);
352
353 let led_on_block = inner_loop_statement.blocks.get(0).unwrap();
354 assert_eq!(led_on_block.block_type, "led_on");
355 assert_eq!(led_on_block.id, "^3xb.m4E9i0;3$R10(=5");
356 assert_eq!(led_on_block.fields.get("TIME"), Some(&FieldValue::SimpleField("300".to_string())));
357
358 let led_off_block = inner_loop_statement.blocks.get(1).unwrap();
359 assert_eq!(led_off_block.block_type, "led_off");
360 assert_eq!(led_off_block.id, "HX4*sB9=gbJtq$Y{ke6b");
361 }
362}