Skip to main content

oak_xml/ast/
mod.rs

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