Skip to main content

oak_xml/ast/
mod.rs

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/// XML AST 根节点
8#[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}