sails_macros_core/export/
mod.rs

1use crate::shared;
2use args::ExportArgs;
3use proc_macro_error::abort;
4use proc_macro2::{Span, TokenStream};
5use syn::{Attribute, ImplItemFn, parse::Parse, spanned::Spanned};
6
7mod args;
8
9pub fn export(attrs: TokenStream, impl_item_fn_tokens: TokenStream) -> TokenStream {
10    let fn_impl: ImplItemFn = syn::parse2::<ImplItemFn>(impl_item_fn_tokens.clone())
11        .unwrap_or_else(|err| {
12            abort!(
13                err.span(),
14                "`export` attribute can be applied to methods only: {}",
15                err
16            )
17        });
18    ensure_pub_visibility(&fn_impl);
19    ensure_single_export_or_route_on_impl(&fn_impl);
20    let args = syn::parse2::<ExportArgs>(attrs)
21        .unwrap_or_else(|_| abort!(fn_impl.span(), "`export` attribute cannot be parsed"));
22    ensure_returns_result_with_unwrap_result(fn_impl, args);
23    impl_item_fn_tokens
24}
25
26fn ensure_pub_visibility(fn_impl: &ImplItemFn) {
27    match fn_impl.vis {
28        syn::Visibility::Public(_) => (),
29        _ => abort!(
30            fn_impl.span(),
31            "`export` attribute can be applied to public methods only"
32        ),
33    }
34}
35
36pub(crate) fn ensure_single_export_or_route_on_impl(fn_impl: &ImplItemFn) {
37    let attr_export = fn_impl.attrs.iter().find(|attr| {
38        attr.meta
39            .path()
40            .segments
41            .last()
42            .map(|s| s.ident == "export")
43            .unwrap_or(false)
44    });
45    if attr_export.is_some() {
46        abort!(
47            fn_impl,
48            "multiple `export` attributes on the same method are not allowed",
49        )
50    }
51}
52
53fn ensure_returns_result_with_unwrap_result(fn_impl: ImplItemFn, args: ExportArgs) {
54    // ensure Result type is returned if unwrap_result is set to true
55    _ = shared::unwrap_result_type(&fn_impl.sig, args.unwrap_result());
56}
57
58pub(crate) fn parse_export_args(attrs: &[Attribute]) -> Option<(ExportArgs, Span)> {
59    let mut attrs = attrs
60        .iter()
61        .filter_map(|attr| parse_attr(attr).map(|args| (args, attr.meta.span())));
62    let export = attrs.next();
63    if let Some((_, span)) = attrs.next() {
64        abort!(
65            span,
66            "multiple `export` attributes are not allowed for the same method"
67        )
68    }
69    export
70}
71
72pub(crate) fn parse_attr(attr: &Attribute) -> Option<ExportArgs> {
73    if attr
74        .path()
75        .segments
76        .last()
77        .is_some_and(|s| s.ident == "export")
78    {
79        if let Ok(list) = attr.meta.require_list() {
80            let args = list
81                .parse_args_with(ExportArgs::parse)
82                .unwrap_or_else(|err| {
83                    abort!(list.span(), "`export` attribute cannot be parsed: {}", err)
84                });
85            Some(args)
86        } else {
87            Some(ExportArgs::default())
88        }
89    } else {
90        None
91    }
92}