apisdk_macros/
lib.rs

1//! A highlevel API client framework for Rust.
2//! This crate is an internal used crate, please check `apisdk` crate for more details.
3
4use parse::parse_meta;
5use quote::quote;
6use syn::{parse_macro_input, DeriveInput, Expr, ItemFn, Meta};
7
8mod build;
9mod parse;
10
11use crate::build::{build_api_impl, build_api_methods, build_builder, build_macro_overrides};
12use crate::parse::parse_fields;
13
14/// Declare a HTTP api with base_url
15///
16/// # Examples
17///
18/// ### Declare
19///
20/// ```
21/// use apisdk::http_api;
22///
23/// #[http_api("https://host.of.service/base/path")]
24/// #[derive(Debug, Clone)]
25/// pub struct MyApi {
26///     any_fields: MustImplDefault
27/// }
28/// ```
29///
30/// ### Define APIs
31///
32/// ```
33/// use apisdk::{ApiResult, send_json};
34/// use serde_json::{json, Value};
35///
36/// impl MyApi {
37///     async fn do_sth(&self, param: u32) -> ApiResult<Value> {
38///         let req = self.post("/relative-path/api").await?;
39///         let payload = json!({
40///             "param": param,
41///         });
42///         send_json!(req, payload).await
43///     }
44/// }
45/// ```
46#[proc_macro_attribute]
47pub fn http_api(
48    meta: proc_macro::TokenStream,
49    input: proc_macro::TokenStream,
50) -> proc_macro::TokenStream {
51    let metadata = parse_meta(meta);
52
53    let ast = parse_macro_input!(input as DeriveInput);
54    let vis = ast.vis;
55    let api_name = ast.ident;
56    let api_attrs = ast.attrs;
57    let (fields_decl, fields_init, fields_clone) = parse_fields(ast.data);
58
59    let (builder_name, builder_impl) =
60        build_builder(&metadata, vis.clone(), api_name.clone(), fields_init);
61    let api_impl = build_api_impl(
62        &metadata,
63        vis.clone(),
64        api_name.clone(),
65        api_attrs,
66        fields_decl,
67        fields_clone,
68        builder_name,
69    );
70    let methods = build_api_methods(vis.clone());
71
72    let output = quote! {
73        #api_impl
74        #builder_impl
75        impl #api_name {
76            #(#methods)*
77        }
78    };
79
80    output.into()
81}
82
83/// Refine a method of HTTP api
84#[proc_macro_attribute]
85pub fn api_method(
86    meta: proc_macro::TokenStream,
87    input: proc_macro::TokenStream,
88) -> proc_macro::TokenStream {
89    let meta = syn::parse_macro_input!(meta as Meta);
90    let log_enabled = if let Meta::NameValue(name_value) = meta {
91        if name_value.path.is_ident("log") {
92            name_value.value
93        } else {
94            syn::parse_str::<Expr>("off").unwrap()
95        }
96    } else {
97        syn::parse_str::<Expr>("off").unwrap()
98    };
99
100    let item_fn = syn::parse_macro_input!(input as ItemFn);
101    let fn_vis = item_fn.vis;
102    let fn_sig = item_fn.sig;
103    let fn_block = item_fn.block;
104
105    let macros = build_macro_overrides(fn_sig.ident.clone());
106
107    let output = quote! {
108        #[allow(unused)]
109        #fn_vis #fn_sig {
110            #(#macros)*
111
112            Self::__REQ_CONFIG.set(apisdk::__internal::RequestConfigurator::new(apisdk::_function_path!(), Some(#log_enabled), false));
113            #fn_block
114        }
115    };
116
117    output.into()
118}
119
120// #[proc_macro_derive(JsonPayload)]
121// pub fn json_payload(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
122//     let input = parse_macro_input!(input as DeriveInput);
123//     let name = input.ident;
124//     let generics = input.generics;
125
126//     let output = if generics.params.is_empty() {
127//         build_simple_json_payload(name)
128//     } else {
129//         // build_simple_json_payload(name)
130//         build_generic_json_payload(name, generics)
131//     };
132
133//     println!("out = {:?}", output);
134//     output.into()
135// }