1use proc_macro2::TokenStream;
2use quote::ToTokens;
3use syn::{
4 parse::{discouraged::Speculative, Parse, ParseStream},
5 parse_quote,
6 punctuated::Punctuated,
7 spanned::Spanned,
8 token::{Brace, Comma, Paren},
9 Attribute, Expr, Lit, Pat, PatType, Token,
10};
11
12use super::{parse::parse_valid_block_expr, InvalidBlock};
13use crate::{
14 node::{NodeBlock, NodeName},
15 parser::recoverable::{ParseRecoverable, RecoverableContext},
16};
17
18#[derive(Clone, Debug, syn_derive::ToTokens)]
19pub struct AttributeValueExpr {
20 pub token_eq: Token![=],
21 pub value: KVAttributeValue,
22}
23
24#[derive(Clone, Debug, syn_derive::ToTokens)]
25pub enum KVAttributeValue {
26 Expr(Expr),
27 InvalidBraced(InvalidBlock),
28}
29
30impl AttributeValueExpr {
31 pub fn value_literal_string(&self) -> Option<String> {
57 match &self.value {
58 KVAttributeValue::Expr(Expr::Lit(l)) => match &l.lit {
59 Lit::Str(s) => Some(s.value()),
60 Lit::Char(c) => Some(c.value().to_string()),
61 Lit::Int(i) => Some(i.base10_digits().to_string()),
62 Lit::Float(f) => Some(f.base10_digits().to_string()),
63 Lit::Bool(b) => Some(b.value.to_string()),
64 _ => None,
65 },
66 _ => None,
67 }
68 }
69}
70
71#[derive(Clone, Debug, syn_derive::ToTokens)]
72pub enum KeyedAttributeValue {
73 Binding(FnBinding),
74 Value(AttributeValueExpr),
75 None,
76}
77
78impl KeyedAttributeValue {
79 pub fn to_value(&self) -> Option<&AttributeValueExpr> {
80 match self {
81 KeyedAttributeValue::Value(v) => Some(v),
82 KeyedAttributeValue::None => None,
83 KeyedAttributeValue::Binding(_) => None,
84 }
85 }
86}
87#[derive(Clone, Debug, syn_derive::ToTokens)]
94pub struct KeyedAttribute {
95 pub key: NodeName,
97 pub possible_value: KeyedAttributeValue,
99}
100impl KeyedAttribute {
101 pub fn value_literal_string(&self) -> Option<String> {
127 self.possible_value
128 .to_value()
129 .and_then(|v| v.value_literal_string())
130 }
131
132 pub fn value(&self) -> Option<&Expr> {
133 self.possible_value
134 .to_value()
135 .map(|v| match &v.value {
136 KVAttributeValue::Expr(expr) => Some(expr),
137 KVAttributeValue::InvalidBraced(_) => None,
138 })
139 .flatten()
140 }
141
142 pub(crate) fn correct_expr_error_span(error: syn::Error, input: ParseStream) -> syn::Error {
146 let error_str = error.to_string();
147 if error_str.starts_with("unexpected end of input") {
148 let stream = input
149 .parse::<TokenStream>()
150 .expect("BUG: Token stream should always be parsable");
151 return syn::Error::new(
152 stream.span(),
153 format!("failed to parse expression: {}", error),
154 );
155 }
156 error
157 }
158}
159
160#[derive(Clone, Debug)]
163pub struct FnBinding {
164 pub paren: Paren,
165 pub inputs: Punctuated<Pat, Comma>,
166}
167
168fn closure_arg(input: ParseStream) -> syn::Result<Pat> {
170 let attrs = input.call(Attribute::parse_outer)?;
171 let mut pat: Pat = Pat::parse_single(input)?;
172
173 if input.peek(Token![:]) {
174 Ok(Pat::Type(PatType {
175 attrs,
176 pat: Box::new(pat),
177 colon_token: input.parse()?,
178 ty: input.parse()?,
179 }))
180 } else {
181 match &mut pat {
182 Pat::Ident(pat) => pat.attrs = attrs,
183 Pat::Lit(pat) => pat.attrs = attrs,
184 Pat::Macro(pat) => pat.attrs = attrs,
185 Pat::Or(pat) => pat.attrs = attrs,
186 Pat::Path(pat) => pat.attrs = attrs,
187 Pat::Range(pat) => pat.attrs = attrs,
188 Pat::Reference(pat) => pat.attrs = attrs,
189 Pat::Rest(pat) => pat.attrs = attrs,
190 Pat::Slice(pat) => pat.attrs = attrs,
191 Pat::Struct(pat) => pat.attrs = attrs,
192 Pat::Tuple(pat) => pat.attrs = attrs,
193 Pat::TupleStruct(pat) => pat.attrs = attrs,
194 Pat::Type(_) => unreachable!("BUG: Type handled in if"),
195 Pat::Verbatim(_) => {}
196 Pat::Wild(pat) => pat.attrs = attrs,
197 _ => unreachable!(),
198 }
199 Ok(pat)
200 }
201}
202
203#[derive(Clone, Debug, syn_derive::ToTokens)]
207pub enum NodeAttribute {
208 Block(NodeBlock),
215 Attribute(KeyedAttribute),
228}
229
230impl ParseRecoverable for KeyedAttribute {
231 fn parse_recoverable(parser: &mut RecoverableContext, input: ParseStream) -> Option<Self> {
232 let key = NodeName::parse(input)
233 .map_err(|e| parser.push_diagnostic(e))
234 .ok()?;
235
236 let possible_value = if input.peek(Paren) {
237 KeyedAttributeValue::Binding(
238 FnBinding::parse(input)
239 .map_err(|e| parser.push_diagnostic(e))
240 .ok()?,
241 )
242 } else if input.peek(Token![=]) {
243 let eq = input
244 .parse::<Token![=]>()
245 .map_err(|e| parser.push_diagnostic(e))
246 .ok()?;
247 if input.is_empty() {
248 parser.push_diagnostic(syn::Error::new(eq.span(), "missing attribute value"));
249 return None;
250 }
251
252 let fork = input.fork();
253
254 let rs = match parse_valid_block_expr(parser, &fork) {
255 Ok(vbl) => {
256 input.advance_to(&fork);
257 KVAttributeValue::Expr(parse_quote!(#vbl))
258 }
259
260 Err(err) if input.fork().peek(Brace) && parser.config().recover_block => {
261 parser.push_diagnostic(err);
262 let ivb = parser.parse_simple(input)?;
263 KVAttributeValue::InvalidBraced(ivb)
264 }
265 Err(_) => {
266 let res = fork
267 .parse::<Expr>()
268 .map_err(|e| {
269 if fork.is_empty() {
273 KeyedAttribute::correct_expr_error_span(e, input)
274 } else {
275 e
276 }
277 })
278 .map_err(|e| parser.push_diagnostic(e))
279 .ok()?;
280
281 input.advance_to(&fork);
282 KVAttributeValue::Expr(res)
283 }
284 };
285
286 KeyedAttributeValue::Value(AttributeValueExpr {
287 token_eq: eq,
288 value: rs,
289 })
290 } else {
291 KeyedAttributeValue::None
292 };
293
294 Some(KeyedAttribute {
295 key,
296 possible_value,
297 })
298 }
299}
300
301impl ParseRecoverable for NodeAttribute {
302 fn parse_recoverable(parser: &mut RecoverableContext, input: ParseStream) -> Option<Self> {
303 let node = if input.peek(Brace) {
304 NodeAttribute::Block(parser.parse_recoverable(input)?)
305 } else {
306 NodeAttribute::Attribute(parser.parse_recoverable(input)?)
307 };
308 Some(node)
309 }
310}
311
312impl Parse for FnBinding {
313 fn parse(stream: ParseStream) -> syn::Result<Self> {
314 let content;
315 let paren = syn::parenthesized!(content in stream);
316 let inputs = Punctuated::parse_terminated_with(&content, closure_arg)?;
317 Ok(Self { paren, inputs })
318 }
319}
320
321impl ToTokens for FnBinding {
322 fn to_tokens(&self, tokens: &mut TokenStream) {
323 self.paren.surround(tokens, |tokens| {
324 self.inputs.to_tokens(tokens);
325 })
326 }
327}