1#![doc = include_str!("readme.md")]
2use crate::{XmlElementType, XmlLanguage, XmlTokenType};
3use core::range::Range;
4use oak_core::{source::Source, tree::RedNode};
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7use std::borrow::Cow;
8
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11#[derive(Clone, Debug, PartialEq)]
12pub struct XmlRoot {
13 pub value: XmlValue,
14}
15
16pub type XmlNode<'a> = RedNode<'a, XmlLanguage>;
17
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19#[derive(Clone, Debug, PartialEq)]
20pub enum XmlValue {
21 Element(XmlElement),
22 Text(String),
23 Comment(String),
24 CData(String),
25 ProcessingInstruction(XmlPI),
26 Fragment(Vec<XmlValue>),
27}
28
29#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30#[derive(Clone, Debug, PartialEq)]
31pub struct XmlElement {
32 pub name: String,
33 pub attributes: Vec<XmlAttribute>,
34 pub children: Vec<XmlValue>,
35 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
36 pub span: Range<usize>,
37}
38
39#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
40#[derive(Clone, Debug, PartialEq)]
41pub struct XmlAttribute {
42 pub name: String,
43 pub value: String,
44 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
45 pub span: Range<usize>,
46}
47
48#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
49#[derive(Clone, Debug, PartialEq)]
50pub struct XmlPI {
51 pub target: String,
52 pub data: Option<String>,
53 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
54 pub span: Range<usize>,
55}
56
57impl XmlValue {
58 pub fn as_element(&self) -> Option<&XmlElement> {
59 match self {
60 XmlValue::Element(e) => Some(e),
61 _ => None,
62 }
63 }
64
65 pub fn as_str(&self) -> Option<&str> {
66 match self {
67 XmlValue::Text(s) => Some(s),
68 _ => None,
69 }
70 }
71
72 pub fn to_string(&self) -> String {
73 match self {
74 XmlValue::Text(t) => t.clone(),
75 XmlValue::Comment(c) => format!("<!--{}-->", c),
76 XmlValue::CData(d) => format!("<![CDATA[{}]]>", d),
77 XmlValue::ProcessingInstruction(pi) => {
78 if let Some(ref data) = pi.data {
79 format!("<?{} {}?>", pi.target, data)
80 }
81 else {
82 format!("<?{}?>", pi.target)
83 }
84 }
85 XmlValue::Fragment(fs) => {
86 let mut s = String::new();
87 for f in fs {
88 s.push_str(&f.to_string());
89 }
90 s
91 }
92 XmlValue::Element(e) => {
93 let mut s = format!("<{}", e.name);
94 for attr in &e.attributes {
95 s.push_str(&format!(" {}=\"{}\"", attr.name, attr.value));
96 }
97 if e.children.is_empty() {
98 s.push_str("/>");
99 }
100 else {
101 s.push('>');
102 for child in &e.children {
103 s.push_str(&child.to_string());
104 }
105 s.push_str(&format!("</{}>", e.name));
106 }
107 s
108 }
109 }
110 }
111}
112
113pub trait XmlNodeExt<'a> {
114 fn tag_name<'s, S: Source + ?Sized>(&self, source: &'s S) -> Option<Cow<'s, str>>;
115 fn attributes<S: Source + ?Sized>(&self, source: &S) -> Vec<(String, String)>;
116 fn xml_children(&self) -> impl Iterator<Item = RedNode<'a, XmlLanguage>>;
117 fn xml_children_recursive(&self) -> impl Iterator<Item = RedNode<'a, XmlLanguage>>;
118 fn text<S: Source + ?Sized>(&self, source: &S) -> String;
119 fn read_attr<S: Source + ?Sized>(&self, source: &S, name: &str) -> Option<String>;
120}
121
122impl<'a> XmlNodeExt<'a> for RedNode<'a, XmlLanguage> {
123 fn tag_name<'s, S: Source + ?Sized>(&self, source: &'s S) -> Option<Cow<'s, str>> {
124 if self.green.kind != XmlElementType::Element {
125 return None;
126 }
127 for child in self.children() {
128 if let Some(node) = child.as_node() {
129 if node.green.kind == XmlElementType::StartTag || node.green.kind == XmlElementType::SelfClosingTag {
130 for gc in node.children() {
131 if let Some(leaf) = gc.as_leaf() {
132 if leaf.kind == XmlTokenType::Identifier {
133 return Some(source.get_text_in(leaf.span));
134 }
135 }
136 }
137 }
138 }
139 }
140 None
141 }
142
143 fn attributes<S: Source + ?Sized>(&self, source: &S) -> Vec<(String, String)> {
144 let mut attrs = Vec::new();
145 if self.green.kind != XmlElementType::Element {
146 return attrs;
147 }
148 for child in self.children() {
149 if let Some(node) = child.as_node() {
150 if node.green.kind == XmlElementType::StartTag || node.green.kind == XmlElementType::SelfClosingTag {
151 for gc in node.children() {
152 if let Some(n) = gc.as_node() {
153 if n.green.kind == XmlElementType::Attribute {
154 let mut name = String::new();
155 let mut value = String::new();
156 for ggc in n.children() {
157 if let Some(leaf) = ggc.as_leaf() {
158 if leaf.kind == XmlTokenType::Identifier {
159 name = source.get_text_in(leaf.span).into_owned();
160 }
161 else if leaf.kind == XmlTokenType::AttributeValue {
162 let v = source.get_text_in(leaf.span);
163 value = v.trim_matches('"').trim_matches('\'').to_string();
164 }
165 }
166 }
167 if !name.is_empty() {
168 attrs.push((name, value));
169 }
170 }
171 }
172 }
173 }
174 }
175 }
176 attrs
177 }
178
179 fn xml_children(&self) -> impl Iterator<Item = RedNode<'a, XmlLanguage>> {
180 self.children().filter_map(|c| c.as_node().filter(|node| node.green.kind == XmlElementType::Element))
181 }
182
183 fn xml_children_recursive(&self) -> impl Iterator<Item = RedNode<'a, XmlLanguage>> {
184 let mut stack = Vec::new();
185 for child in self.xml_children() {
186 stack.push(child);
187 }
188
189 std::iter::from_fn(move || {
190 let next = stack.pop()?;
191 let children = next.xml_children().collect::<Vec<_>>();
192 for child in children.into_iter().rev() {
193 stack.push(child);
194 }
195 Some(next)
196 })
197 }
198
199 fn text<S: Source + ?Sized>(&self, source: &S) -> String {
200 let mut text = String::new();
201 for child in self.children() {
202 if let Some(leaf) = child.as_leaf() {
203 if leaf.kind == XmlTokenType::Text {
204 text.push_str(&source.get_text_in(leaf.span));
205 }
206 }
207 else if let Some(node) = child.as_node() {
208 if node.green.kind == XmlElementType::Element {
209 text.push_str(&node.text(source));
210 }
211 }
212 }
213 text
214 }
215
216 fn read_attr<S: Source + ?Sized>(&self, source: &S, name: &str) -> Option<String> {
217 self.attributes(source).into_iter().find(|(n, _)| n == name).map(|(_, v)| v)
218 }
219}