lets_expect_core/core/
when.rs1use proc_macro2::{Ident, Span, TokenStream};
2use syn::punctuated::Punctuated;
3use syn::spanned::Spanned;
4use syn::token::{Brace, Comma, Paren};
5use syn::{braced, parenthesized, parse::Parse};
6
7use crate::utils::to_ident::local_to_ident;
8
9use super::context::Context;
10use super::create_module::create_module;
11use super::keyword;
12use super::runtime::Runtime;
13use syn::{Attribute, Expr, Local, Pat, Type};
14use syn::{PatType, Token};
15
16const WHEN_IDENT_PREFIX: &str = "when_";
17
18struct WhenLet {
19 pub attrs: Vec<Attribute>,
20 pub pat: Pat,
21 pub init: (Token![=], Box<Expr>),
22}
23
24impl Parse for WhenLet {
25 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
26 let attrs = input.call(Attribute::parse_outer)?;
27 let mut pat = input.parse()?;
28
29 if input.peek(Token![:]) {
30 let colon_token: Token![:] = input.parse()?;
31 let ty: Type = input.parse()?;
32 pat = Pat::Type(PatType {
33 attrs: Vec::new(),
34 pat: Box::new(pat),
35 colon_token,
36 ty: Box::new(ty),
37 });
38 }
39
40 let init = (input.parse()?, input.parse()?);
41 Ok(Self { attrs, pat, init })
42 }
43}
44
45impl WhenLet {
46 pub fn to_local(&self) -> Local {
47 Local {
48 attrs: self.attrs.clone(),
49 let_token: Default::default(),
50 pat: self.pat.clone(),
51 init: Some(self.init.clone()),
52 semi_token: Default::default(),
53 }
54 }
55}
56
57pub struct When {
58 context: Context,
59 identifier: Ident,
60 string: String,
61 lets: Vec<Local>,
62}
63
64impl Parse for When {
65 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
66 let (lets, identifier, string) = if input.peek(Paren) {
67 parse_lets_in_parentheses(input)?
68 } else {
69 let identifier = input.parse::<Ident>()?;
70 (
71 Vec::new(),
72 Ident::new(
73 &format!("{}{}", WHEN_IDENT_PREFIX, identifier),
74 identifier.span(),
75 ),
76 identifier.to_string(),
77 )
78 };
79
80 let identifier = if input.peek(Token![as]) {
81 input.parse::<Token![as]>()?;
82 let ident = input.parse::<Ident>()?;
83 Ident::new(&format!("{}{}", WHEN_IDENT_PREFIX, ident), ident.span())
84 } else {
85 identifier
86 };
87
88 let context = if input.peek(Brace) {
89 let content;
90 braced!(content in input);
91 content.parse::<Context>()?
92 } else {
93 Context::from_single_item(input)?
94 };
95
96 Ok(Self {
97 lets,
98 identifier,
99 string,
100 context,
101 })
102 }
103}
104
105fn parse_lets_in_parentheses(
106 input: &syn::parse::ParseBuffer,
107) -> Result<(Vec<Local>, Ident, String), syn::Error> {
108 let content;
109 parenthesized!(content in input);
110
111 let string = content.to_string();
112 let when_lets: Punctuated<WhenLet, Comma> = Punctuated::parse_separated_nonempty(&content)?;
113 let lets: Vec<Local> = when_lets.iter().map(WhenLet::to_local).collect();
114
115 if lets.is_empty() {
116 return Err(syn::Error::new(
117 Span::call_site(),
118 "Expected at least one assignment",
119 ));
120 }
121
122 let name = WHEN_IDENT_PREFIX.to_string()
123 + lets
124 .iter()
125 .map(local_to_ident)
126 .collect::<Vec<String>>()
127 .join("_")
128 .as_str();
129 let identifier = Ident::new(name.as_str(), input.span());
130 Ok((lets, identifier, string))
131}
132
133impl When {
134 pub fn to_tokens(&self, keyword: &keyword::when, runtime: &Runtime) -> TokenStream {
135 let runtime = runtime.add_when(self.string.clone()).add_lets(&self.lets);
136 let context = self.context.to_tokens(&keyword.span(), &runtime);
137 create_module(&keyword.span(), &self.identifier, &context)
138 }
139}