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