1use proc_macro2::{Delimiter, TokenStream as TokenStream2, TokenTree};
2use quote::ToTokens;
3use std::hash;
4use syn::{parse::Parse, spanned::Spanned, token::Brace, Expr};
5
6#[derive(Clone, Debug)]
11pub struct PartialExpr {
12 pub brace: Option<Brace>,
13 pub expr: TokenStream2,
14}
15
16impl Parse for PartialExpr {
17 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
18 let mut is_braced = false;
26 if let Some((TokenTree::Group(group), next)) = input.fork().cursor().token_tree() {
27 let next_char_is_a_comma = next.punct().is_some_and(|(tt, _)| tt.as_char() == ',');
28 let next_is_a_braced_exp = next.group(Delimiter::Brace).is_some();
29 let next_is_an_ident = next.ident().is_some();
30 let next_is_a_string_literal = next.literal().is_some();
31
32 if group.delimiter() == Delimiter::Brace
33 && (next.eof()
34 || next_char_is_a_comma
35 || next_is_a_braced_exp
36 || next_is_an_ident
37 || next_is_a_string_literal)
38 {
39 is_braced = true
40 }
41 };
42
43 if !is_braced {
45 let expr = input.parse::<syn::Expr>()?;
46 return Ok(Self {
47 brace: None,
48 expr: expr.to_token_stream(),
49 });
50 }
51
52 let content;
53 let brace = Some(syn::braced!(content in input));
54 let expr: TokenStream2 = content.parse()?;
55
56 Ok(Self { brace, expr })
57 }
58}
59
60impl ToTokens for PartialExpr {
61 fn to_tokens(&self, tokens: &mut TokenStream2) {
62 match &self.brace {
63 Some(brace) => brace.surround(tokens, |tokens| self.expr.to_tokens(tokens)),
64 _ => self.expr.to_tokens(tokens),
65 }
66 }
67}
68
69impl PartialExpr {
70 pub fn as_expr(&self) -> syn::Result<syn::Expr> {
71 if let Some(brace) = &self.brace {
74 let mut tokens = TokenStream2::new();
75 let f = |tokens: &mut TokenStream2| self.expr.to_tokens(tokens);
76 brace.surround(&mut tokens, f);
77 return syn::parse2(tokens);
78 }
79
80 let expr = self.expr.clone();
81 syn::parse2(expr.to_token_stream())
82 }
83
84 pub fn from_expr(expr: &Expr) -> Self {
85 Self {
86 brace: None,
87 expr: expr.to_token_stream(),
88 }
89 }
90 pub fn span(&self) -> proc_macro2::Span {
91 if let Some(brace) = &self.brace {
92 brace.span.span()
93 } else {
94 self.expr.span()
95 }
96 }
97}
98
99impl PartialEq for PartialExpr {
100 fn eq(&self, other: &Self) -> bool {
101 self.expr.to_string() == other.expr.to_string()
102 }
103}
104
105impl Eq for PartialExpr {}
106
107impl hash::Hash for PartialExpr {
108 fn hash<H: hash::Hasher>(&self, state: &mut H) {
109 self.expr.to_string().hash(state);
110 }
111}