sails_macros_core/export/
mod.rs1use 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 _ = 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}