Skip to main content

viral32111_xml/
element.rs

1use std::error::Error;
2
3use super::attributes::{self, Attributes};
4
5pub struct Element {
6	pub name: Option<String>,
7	pub value: Option<String>,
8	pub attributes: Option<Attributes>,
9	pub children: Option<Vec<Element>>,
10}
11
12impl Element {}
13
14/// Parses the first element in an XML document.
15pub fn parse(text: &str) -> Result<(Element, usize), Box<dyn Error>> {
16	// Don't bother if we have nothing
17	if text.is_empty() {
18		return Err("Empty XML element".into());
19	}
20
21	// We're not an element, we're inner text
22	if !text.starts_with("<") && !text.ends_with(">") {
23		return Ok((
24			Element {
25				name: None,
26				value: Some(text.to_string()),
27				attributes: None,
28				children: None,
29			},
30			text.len(),
31		));
32	}
33
34	// Find where I begin
35	let mut opening_tag_end_position = text.find(">").expect("Tag not terminated");
36
37	// Back up if we're self-closing
38	let is_self_closing = text[opening_tag_end_position - 1..opening_tag_end_position].eq("/");
39	if is_self_closing {
40		opening_tag_end_position -= 1;
41	}
42
43	let opening_tag = &text[1..opening_tag_end_position];
44
45	// Extract the name
46	let name_end_position = opening_tag.find(" ").unwrap_or(opening_tag.len());
47	let name = &opening_tag[..name_end_position];
48
49	// Parse the attributes
50	let attributes = if opening_tag.contains(" ") {
51		Some(attributes::parse(&opening_tag[name_end_position..]))
52	} else {
53		None
54	};
55
56	// Don't continue if we're self-closing, as there won't be a closing tag & children
57	if is_self_closing {
58		return Ok((
59			Element {
60				name: Some(name.to_string()),
61				value: None,
62				attributes,
63				children: None,
64			},
65			opening_tag_end_position + (if is_self_closing { 2 } else { 1 }),
66		));
67	}
68
69	// Find where I end
70	let closing_tag_position = text
71		.find(&format!("</{}>", name))
72		.expect("Element not terminated");
73
74	// Parse the top-level children
75	let inner_text = &text[opening_tag_end_position + 1..closing_tag_position];
76	let mut children = Vec::new();
77	let mut position = 0;
78	loop {
79		let (child, child_end_position) = parse(&inner_text[position..])?;
80		position += child_end_position;
81		children.push(child);
82
83		if position >= inner_text.len() {
84			break;
85		}
86	}
87
88	Ok((
89		Element {
90			name: Some(name.to_string()),
91			value: None,
92			attributes,
93			children: Some(children),
94		},
95		closing_tag_position + name.len() + 3, // Skip past </name>
96	))
97}