sycamore_view_parser/
parse.rs

1//! Parse syntax for `view!` macro.
2
3use syn::ext::IdentExt;
4use syn::parse::{Parse, ParseStream};
5use syn::token::{Brace, Paren};
6use syn::{braced, parenthesized, token, Ident, LitStr, Result, Token};
7
8use crate::ir::*;
9
10impl Parse for Root {
11    fn parse(input: ParseStream) -> Result<Self> {
12        let mut children = Vec::new();
13
14        while !input.is_empty() {
15            children.push(input.parse()?);
16        }
17
18        Ok(Self(children))
19    }
20}
21
22impl Node {
23    fn peek_type(input: ParseStream) -> Option<NodeType> {
24        let input = input.fork(); // do not affect original ParseStream
25
26        if input.peek(LitStr) {
27            Some(NodeType::Text)
28        } else if input.peek(Paren) {
29            Some(NodeType::Dyn)
30        } else if input.peek(Token![::]) || input.peek(Ident::peek_any) {
31            Some(NodeType::Tag)
32        } else {
33            None
34        }
35    }
36}
37
38impl Parse for Node {
39    fn parse(input: ParseStream) -> Result<Self> {
40        let ty = match Self::peek_type(input) {
41            Some(ty) => ty,
42            None => return Err(input.error("expected a valid node")),
43        };
44
45        Ok(match ty {
46            NodeType::Tag => Self::Tag(input.parse()?),
47            NodeType::Text => Self::Text(input.parse()?),
48            NodeType::Dyn => Self::Dyn(input.parse()?),
49        })
50    }
51}
52
53impl Parse for TagNode {
54    fn parse(input: ParseStream) -> Result<Self> {
55        let ident = input.parse()?;
56
57        let has_paren = input.peek(token::Paren);
58        let attrs = if has_paren {
59            let content;
60            parenthesized!(content in input);
61            content
62                .parse_terminated(Prop::parse, Token![,])?
63                .into_iter()
64                .collect()
65        } else {
66            Vec::new()
67        };
68
69        if !has_paren && !input.peek(Brace) {
70            return Err(input.error("expected either `(` or `{` after element tag"));
71        }
72
73        let mut children = Vec::new();
74        if input.peek(Brace) {
75            let content;
76            braced!(content in input);
77            while !content.is_empty() {
78                children.push(content.parse()?);
79            }
80        }
81
82        Ok(Self {
83            ident,
84            props: attrs,
85            children: Root(children),
86        })
87    }
88}
89
90impl Parse for TagIdent {
91    fn parse(input: ParseStream) -> Result<Self> {
92        let is_hyphenated = input.peek2(Token![-]);
93        if is_hyphenated {
94            let mut segments: Vec<Ident> = vec![input.call(Ident::parse_any)?];
95            while input.peek(Token![-]) {
96                let _: Token![-] = input.parse()?;
97                segments.push(input.parse()?);
98            }
99            let tag = segments
100                .into_iter()
101                .map(|i| i.to_string())
102                .collect::<Vec<_>>()
103                .join("-");
104            Ok(Self::Hyphenated(tag))
105        } else {
106            Ok(Self::Path(input.parse()?))
107        }
108    }
109}
110
111impl Parse for Prop {
112    fn parse(input: ParseStream) -> Result<Self> {
113        let span = input.span();
114        let ty = input.parse()?;
115        if !matches!(ty, PropType::Spread) {
116            let _eqs: Token![=] = input.parse()?;
117        }
118        let value = input.parse()?;
119        Ok(Self { ty, value, span })
120    }
121}
122
123impl Parse for PropType {
124    fn parse(input: ParseStream) -> Result<Self> {
125        let lookahead = input.lookahead1();
126        if lookahead.peek(Token![..]) {
127            let _2dot = input.parse::<Token![..]>()?;
128            Ok(Self::Spread)
129        } else if lookahead.peek(Ident::peek_any) {
130            // Check if we are parsing a hyphenated attribute.
131            if input.peek2(Token![-]) {
132                let mut segments: Vec<Ident> = vec![input.call(Ident::parse_any)?];
133                while input.peek(Token![-]) {
134                    let _: Token![-] = input.parse()?;
135                    segments.push(input.call(Ident::parse_any)?);
136                }
137                let ident = segments
138                    .into_iter()
139                    .map(|i| i.to_string())
140                    .collect::<Vec<_>>()
141                    .join("-");
142                Ok(Self::PlainHyphenated { ident })
143            } else {
144                let name: Ident = input.call(Ident::parse_any)?;
145
146                if name == "ref" {
147                    Ok(Self::Ref)
148                } else if input.peek(Token![:]) {
149                    let _colon: Token![:] = input.parse()?;
150                    let ident = input.call(Ident::parse_any)?;
151                    Ok(Self::Directive { dir: name, ident })
152                } else {
153                    Ok(Self::Plain { ident: name })
154                }
155            }
156        } else if lookahead.peek(LitStr) {
157            let name: String = <LitStr as Parse>::parse(input).map(|s| s.value())?;
158            Ok(Self::PlainQuoted { ident: name })
159        } else {
160            Err(lookahead.error())
161        }
162    }
163}
164
165impl Parse for TextNode {
166    fn parse(input: ParseStream) -> Result<Self> {
167        Ok(Self {
168            value: input.parse()?,
169        })
170    }
171}
172
173impl Parse for DynNode {
174    fn parse(input: ParseStream) -> Result<Self> {
175        let content;
176        parenthesized!(content in input);
177        Ok(Self {
178            value: content.parse()?,
179        })
180    }
181}