1 use proc_macro2::{Span, TokenStream};
2 use quote::ToTokens;
3 use syn::parse::{Parse, ParseStream};
4 use syn::{Attribute, Error, Expr, Fields, Result, Stmt, Token, Visibility};
5 
6 use crate::emit::Kind;
7 
8 pub enum Input {
9     Enum(syn::ItemEnum),
10     Match(syn::ExprMatch),
11     Struct(syn::ItemStruct),
12     Let(syn::ExprMatch),
13 }
14 
15 impl Input {
kind(&self) -> Kind16     pub fn kind(&self) -> Kind {
17         match self {
18             Input::Enum(_) => Kind::Enum,
19             Input::Match(_) => Kind::Match,
20             Input::Struct(_) => Kind::Struct,
21             Input::Let(_) => Kind::Let,
22         }
23     }
24 }
25 
26 impl Parse for Input {
parse(input: ParseStream) -> Result<Self>27     fn parse(input: ParseStream) -> Result<Self> {
28         let ahead = input.fork();
29         let _ = ahead.call(Attribute::parse_outer)?;
30 
31         if ahead.peek(Token![match]) {
32             let expr = match input.parse()? {
33                 Expr::Match(expr) => expr,
34                 _ => unreachable!("expected match"),
35             };
36             return Ok(Input::Match(expr));
37         }
38 
39         if ahead.peek(Token![let]) {
40             let stmt = match input.parse()? {
41                 Stmt::Local(stmt) => stmt,
42                 _ => unreachable!("expected let"),
43             };
44             let init = match stmt.init {
45                 Some(init) => init,
46                 None => return Err(unexpected()),
47             };
48             let expr = match *init.expr {
49                 Expr::Match(expr) => expr,
50                 _ => return Err(unexpected()),
51             };
52             return Ok(Input::Let(expr));
53         }
54 
55         let _: Visibility = ahead.parse()?;
56         if ahead.peek(Token![enum]) {
57             return input.parse().map(Input::Enum);
58         } else if ahead.peek(Token![struct]) {
59             let input: syn::ItemStruct = input.parse()?;
60             if let Fields::Named(_) = &input.fields {
61                 return Ok(Input::Struct(input));
62             }
63         }
64 
65         Err(unexpected())
66     }
67 }
68 
69 impl ToTokens for Input {
to_tokens(&self, tokens: &mut TokenStream)70     fn to_tokens(&self, tokens: &mut TokenStream) {
71         match self {
72             Input::Enum(item) => item.to_tokens(tokens),
73             Input::Struct(item) => item.to_tokens(tokens),
74             Input::Match(expr) | Input::Let(expr) => expr.to_tokens(tokens),
75         }
76     }
77 }
78 
unexpected() -> Error79 fn unexpected() -> Error {
80     let span = Span::call_site();
81     let msg = "expected enum, struct, or match expression";
82     Error::new(span, msg)
83 }
84