rocket_codegen/attribute/catch/
parse.rs

1use devise::ext::SpanDiagnosticExt;
2use devise::{MetaItem, Spanned, Result, FromMeta, Diagnostic};
3use proc_macro2::TokenStream;
4
5use crate::{http, http_codegen};
6
7/// This structure represents the parsed `catch` attribute and associated items.
8pub struct Attribute {
9    /// The status associated with the code in the `#[catch(code)]` attribute.
10    pub status: Option<http::Status>,
11    /// The function that was decorated with the `catch` attribute.
12    pub function: syn::ItemFn,
13}
14
15/// We generate a full parser for the meta-item for great error messages.
16#[derive(FromMeta)]
17struct Meta {
18    #[meta(naked)]
19    code: Code,
20}
21
22/// `Some` if there's a code, `None` if it's `default`.
23#[derive(Debug)]
24struct Code(Option<http::Status>);
25
26impl FromMeta for Code {
27    fn from_meta(meta: &MetaItem) -> Result<Self> {
28        if usize::from_meta(meta).is_ok() {
29            let status = http_codegen::Status::from_meta(meta)?;
30            Ok(Code(Some(status.0)))
31        } else if let MetaItem::Path(path) = meta {
32            if path.is_ident("default") {
33                Ok(Code(None))
34            } else {
35                Err(meta.span().error("expected `default`"))
36            }
37        } else {
38            let msg = format!("expected integer or `default`, found {}", meta.description());
39            Err(meta.span().error(msg))
40        }
41    }
42}
43
44impl Attribute {
45    pub fn parse(args: TokenStream, input: proc_macro::TokenStream) -> Result<Self> {
46        let function: syn::ItemFn = syn::parse(input)
47            .map_err(Diagnostic::from)
48            .map_err(|diag| diag.help("`#[catch]` can only be used on functions"))?;
49
50        let attr: MetaItem = syn::parse2(quote!(catch(#args)))?;
51        let status = Meta::from_meta(&attr)
52            .map(|meta| meta.code.0)
53            .map_err(|diag| diag.help("`#[catch]` expects a status code int or `default`: \
54                        `#[catch(404)]` or `#[catch(default)]`"))?;
55
56        Ok(Attribute { status, function })
57    }
58}