rstml/node/
mod.rs

1//! Tree of nodes.
2
3use std::{convert, fmt};
4
5use atoms::{tokens, FragmentClose, FragmentOpen};
6use proc_macro2::{Ident, TokenStream};
7use quote::ToTokens;
8use syn::{parse::ParseStream, ExprPath, LitStr, Token};
9
10pub mod atoms;
11mod attribute;
12mod node_name;
13mod node_value;
14pub mod parse;
15mod parser_ext;
16mod raw_text;
17
18pub use attribute::{
19    AttributeValueExpr, FnBinding, KVAttributeValue, KeyedAttribute, KeyedAttributeValue,
20    NodeAttribute,
21};
22pub use node_name::{NodeName, NodeNameFragment};
23pub use node_value::{InvalidBlock, NodeBlock};
24
25pub use self::raw_text::RawText;
26use crate::recoverable::{ParseRecoverable, RecoverableContext};
27
28/// Node types.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum NodeType {
31    Element,
32    Text,
33    Comment,
34    Doctype,
35    Block,
36    Fragment,
37    RawText,
38    Custom,
39}
40
41impl fmt::Display for NodeType {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        write!(
44            f,
45            "{}",
46            match self {
47                Self::Element => "NodeType::Element",
48                Self::Text => "NodeType::Text",
49                Self::RawText => "NodeType::RawText",
50                Self::Comment => "NodeType::Comment",
51                Self::Doctype => "NodeType::Doctype",
52                Self::Block => "NodeType::Block",
53                Self::Fragment => "NodeType::Fragment",
54                Self::Custom => "NodeType::Custom",
55            }
56        )
57    }
58}
59
60/// Node in the tree.
61#[derive(Clone, Debug)]
62pub enum Node<C = Infallible> {
63    Comment(NodeComment),
64    Doctype(NodeDoctype),
65    Fragment(NodeFragment<C>),
66    Element(NodeElement<C>),
67    Block(NodeBlock),
68    Text(NodeText),
69    RawText(RawText<C>),
70    Custom(C),
71}
72// Manual implementation, because derive macro doesn't support generics.
73impl<C: CustomNode> ToTokens for Node<C> {
74    fn to_tokens(&self, tokens: &mut TokenStream) {
75        match self {
76            Self::Comment(comment) => comment.to_tokens(tokens),
77            Self::Doctype(doctype) => doctype.to_tokens(tokens),
78            Self::Fragment(fragment) => fragment.to_tokens(tokens),
79            Self::Element(element) => element.to_tokens(tokens),
80            Self::Block(block) => block.to_tokens(tokens),
81            Self::Text(text) => text.to_tokens(tokens),
82            Self::RawText(raw_text) => raw_text.to_tokens(tokens),
83            Self::Custom(custom) => custom.to_tokens(tokens),
84        }
85    }
86}
87
88impl<C: CustomNode> Node<C> {
89    pub fn flatten(mut self) -> Vec<Self> {
90        let children = self
91            .children_mut()
92            .map(|children| children.drain(..))
93            .into_iter()
94            .flatten()
95            .collect::<Vec<_>>();
96
97        std::iter::once(self)
98            .chain(children.into_iter().flat_map(Self::flatten))
99            .collect()
100    }
101    /// Get the type of the node.
102    pub fn r#type(&self) -> NodeType {
103        match &self {
104            Self::Element(_) => NodeType::Element,
105            Self::Text(_) => NodeType::Text,
106            Self::Comment(_) => NodeType::Comment,
107            Self::Doctype(_) => NodeType::Element,
108            Self::Block(_) => NodeType::Block,
109            Self::Fragment(_) => NodeType::Fragment,
110            Self::RawText(_) => NodeType::RawText,
111            Self::Custom(_) => NodeType::Custom,
112        }
113    }
114
115    /// Get node children.
116    pub fn children(&self) -> Option<&Vec<Self>> {
117        match self {
118            Self::Fragment(NodeFragment { children, .. })
119            | Self::Element(NodeElement { children, .. }) => Some(children),
120            _ => None,
121        }
122    }
123
124    /// Get mutable node children.
125    pub fn children_mut(&mut self) -> Option<&mut Vec<Self>> {
126        match self {
127            Self::Fragment(NodeFragment { children, .. })
128            | Self::Element(NodeElement { children, .. }) => Some(children),
129            _ => None,
130        }
131    }
132}
133
134/// Element node.
135///
136/// A HTMLElement tag, with optional children and attributes.
137/// Potentially selfclosing. Any tag name is valid.
138#[derive(Clone, Debug)]
139pub struct NodeElement<C> {
140    pub open_tag: atoms::OpenTag,
141    pub children: Vec<Node<C>>,
142    pub close_tag: Option<atoms::CloseTag>,
143}
144// Manual implementation, because derive macro doesn't support generics.
145impl<C: CustomNode> ToTokens for NodeElement<C> {
146    fn to_tokens(&self, tokens: &mut TokenStream) {
147        self.open_tag.to_tokens(tokens);
148        for child in &self.children {
149            child.to_tokens(tokens);
150        }
151        if let Some(close_tag) = &self.close_tag {
152            close_tag.to_tokens(tokens);
153        }
154    }
155}
156
157impl<C: CustomNode> NodeElement<C> {
158    pub fn name(&self) -> &NodeName {
159        &self.open_tag.name
160    }
161    pub fn attributes(&self) -> &[NodeAttribute] {
162        &self.open_tag.attributes
163    }
164    pub fn attributes_mut(&mut self) -> &mut Vec<NodeAttribute> {
165        &mut self.open_tag.attributes
166    }
167    pub fn chidlren(&self) -> &[Node<C>] {
168        &self.children
169    }
170    pub fn children_mut(&mut self) -> &mut Vec<Node<C>> {
171        &mut self.children
172    }
173}
174
175/// Text node.
176///
177/// Quoted text. Unquoted can be found in `RawText`.
178#[derive(Clone, Debug, syn_derive::Parse, syn_derive::ToTokens)]
179pub struct NodeText {
180    /// The text value.
181    pub value: LitStr,
182}
183
184impl NodeText {
185    /// Returns value of inner LitStr
186    pub fn value_string(&self) -> String {
187        self.value.value()
188    }
189}
190
191/// Comment node.
192///
193/// Comment: `<!-- "comment" -->`, currently has the same restrictions as
194/// `Text` (comment needs to be quoted).
195#[derive(Clone, Debug, syn_derive::Parse, syn_derive::ToTokens)]
196pub struct NodeComment {
197    pub token_start: tokens::ComStart,
198    /// The comment value.
199    pub value: LitStr,
200    pub token_end: tokens::ComEnd,
201}
202/// Doctype node.
203///
204/// Doctype declaration: `<!DOCTYPE html>` (case insensitive), `html` is the
205/// node value in this case.
206/// Usually doctype only contaim html, but also can contain arbitrary DOCTYPE
207/// legacy string, or "obsolete permitted DOCTYPE string", therewhy value is
208/// RawText.
209#[derive(Clone, Debug, syn_derive::ToTokens)]
210pub struct NodeDoctype {
211    pub token_start: tokens::DocStart,
212    /// "doctype"
213    pub token_doctype: Ident,
214    /// The doctype value.
215    pub value: RawText,
216    pub token_end: Token![>],
217}
218
219/// Fragement node.
220///
221/// Fragment: `<></>`
222#[derive(Clone, Debug)]
223pub struct NodeFragment<C> {
224    /// Open fragment token
225    pub tag_open: FragmentOpen,
226    /// Children of the fragment node.
227    pub children: Vec<Node<C>>,
228    /// Close fragment token
229    pub tag_close: Option<FragmentClose>,
230}
231// Manual implementation, because derive macro doesn't support generics.
232impl<C: CustomNode> ToTokens for NodeFragment<C> {
233    fn to_tokens(&self, tokens: &mut TokenStream) {
234        self.tag_open.to_tokens(tokens);
235        for child in &self.children {
236            child.to_tokens(tokens);
237        }
238        if let Some(close_tag) = &self.tag_close {
239            close_tag.to_tokens(tokens);
240        }
241    }
242}
243
244impl<C> NodeFragment<C> {
245    pub fn children(&self) -> &[Node<C>] {
246        &self.children
247    }
248    pub fn children_mut(&mut self) -> &mut Vec<Node<C>> {
249        &mut self.children
250    }
251}
252
253fn path_to_string(expr: &ExprPath) -> String {
254    expr.path
255        .segments
256        .iter()
257        .map(|segment| segment.ident.to_string())
258        .collect::<Vec<String>>()
259        .join("::")
260}
261
262pub trait CustomNode: ParseRecoverable + ToTokens {
263    /// Peeks the token stream to decide whether this node should be parsed.
264    ///
265    /// Recieves a [`ParseStream::fork`].
266    ///
267    /// [`ParseStream::fork`]: syn::parse::ParseBuffer::fork
268    fn peek_element(input: ParseStream) -> bool;
269}
270
271/// Newtype for `std::convert::Infallible` used to implement
272/// `ToTokens` for `Infallible`.
273#[derive(Clone, Copy, Debug, Eq, PartialEq)]
274pub struct Infallible(convert::Infallible);
275
276impl From<convert::Infallible> for Infallible {
277    fn from(s: convert::Infallible) -> Self {
278        match s {}
279    }
280}
281impl ToTokens for Infallible {
282    fn to_tokens(&self, _tokens: &mut TokenStream) {
283        match self.0 {}
284    }
285}
286impl ParseRecoverable for Infallible {
287    fn parse_recoverable(_: &mut RecoverableContext, _: ParseStream) -> Option<Self> {
288        unreachable!("Infallible::peek_element returns false")
289    }
290}
291impl CustomNode for Infallible {
292    fn peek_element(_: ParseStream) -> bool {
293        false
294    }
295}