1use quote::quote;
2use syn::parse::discouraged::Speculative;
3
4
5#[proc_macro]
18pub fn let_else(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
19 enum RestPat {
20 Pattern(syn::Pat),
21 Ident(syn::Ident),
22 }
23
24 impl syn::parse::Parse for RestPat {
25 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
26 if let Err(_) = input.parse::<syn::Token![as]>() {
27 return Ok(Self::Pattern(syn::parse_quote! { _ }));
28 }
29
30 let fork = input.fork();
31 if let Ok(pattern) = fork.call(syn::Pat::parse_multi_with_leading_vert) {
32 input.advance_to(&fork);
33 return Ok(Self::Pattern(pattern));
34 }
35
36 if let Ok(ident) = input.parse() {
37 return Ok(Self::Ident(ident));
38 }
39
40 Err(input.error("Expected a pattern or an identifier after `as`"))
41 }
42 }
43
44 impl quote::ToTokens for RestPat {
45 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
46 match self {
47 RestPat::Pattern(pat) => pat.to_tokens(tokens),
48 RestPat::Ident(ident) => ident.to_tokens(tokens),
49 }
50 }
51 }
52
53 struct Input {
54 pattern: syn::Pat,
55 _sym_eq: syn::Token![=],
56 expr: syn::Expr,
57 _kw_else: syn::Token![else],
58 rest: RestPat,
59 block: syn::Block,
60 }
61
62 impl syn::parse::Parse for Input {
63 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
64 Ok(Self {
65 pattern: input.call(syn::Pat::parse_single)?,
66 _sym_eq: input.parse()?,
67 expr: input.parse()?,
68 _kw_else: input.parse()?,
69 rest: input.parse()?,
70 block: input.parse()?,
71 })
72 }
73 }
74
75 let Input { pattern, expr, rest, block, .. } = syn::parse_macro_input!(input as Input);
76
77 quote! {
78 #[doc(hidden)]
79 let __let_else_expr__ = #expr;
80 let #pattern = __let_else_expr__ else {
81 match __let_else_expr__ {
82 #[allow(unused, dead_code)]
83 #pattern => unsafe { std::hint::unreachable_unchecked() },
84 #rest => #block,
85 }
86 };
87 }.into()
88}