1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#![forbid(unsafe_code)]
mod derive_config;
mod derive_provider;
mod from_state_collector;
mod prepare_macro;

use prepare_macro::inputs::attr_name::PrepareName;
use syn::{parse_macro_input, DeriveInput, ItemFn};

#[macro_use]
mod utils;

/// implement [`Provider<T>`](https://docs.rs/axum-starter/latest/axum_starter/trait.Provider.html) for each field of the struct
///
/// ## Example
///
/// ```rust
/// #[derive(Debug, Provider)]
/// #[provider(ref)]
/// struct Configure {
///     // this will impl `Provider<&String>`
///     // because of the `ref` on container and its own `transparent`
///     #[provider(transparent)]
///     foo: String,
///     // this will not impl provide
///     #[provider(skip)]
///     bar: SocketAddr,
///     // this will impl `Provide<FooBar>`
///     // where `FooBar` is `struct FooBar((i32,i32));`
///     // `ignore_global` will ignore the `ref` on the container
///     #[provider(ignore_global)]
///     foo_bar: (i32, i32),
/// }
///
/// fn foo_fetch(foo: &String, FooBar(foo_bar): FooBar){
///     // do somethings
/// }
///
/// ```  
///
/// - using `ref` to impl `Provider` provide reference instant of Owned (with clone) .Can be using on container to apply on all fields
/// - using `transparent` to impl `Provider` the original type instant of generate a wrapper type. Can be using on container to apply on all fields
/// - using `ignore_global` to ignore the `ref` and `transparent` setting on container
/// - using `skip` to not impl `Provider` for this field
/// - using `map_to(ty , by)` to adding extra provide for [Type](syn::Type) by the giving function, if the type need lifetime mark,
/// adding `lifetime = "'a"`, then using`'a` in your type for example `& 'a str`
#[proc_macro_derive(Provider, attributes(provider))]
pub fn derive_config_provider(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let derive_input = parse_macro_input!(input as DeriveInput);
    darling_err!(derive_provider::provider_derive(derive_input))
}

/// help macro from impl `ServeAddress`, `LoggerInitialization`, `ConfigureServerEffect`
///
/// ## Example
///
/// ```rust
/// #[derive(Debug, Provider, Configure)]
/// #[conf(
///     address(provide),
///     logger(error = "log::SetLoggerError", func = "Self::init_log"),
///     server
///)]
/// struct Configure {
///     #[provider(transparent)]
///     bar: SocketAddr,
/// }
///
/// impl Configure {
///     fn init_log(&self) -> Result<(), log::SetLoggerError>{
///         // initial the logger
///         Ok(())
///     }
/// }
///
/// ```  
/// ## Usage
/// ### address
/// - using `address(provide)` direct using the config provide to get address,
/// - using `address(provide(ty = "..."))` similar to previous one, but using the provide type
///     **Note**: the provided type need impl [Into<std::net::SocketAddr>](Into<std::net::SocketAddr>)
///
/// - using `address(func(path = "...", ty = "...", associate))` using provide function get the socket address
///     - `path` a path to a function or a closure expr, its signature is `Fn(config: &Self) -> $ty`
///     - `ty` (optional) default is [std::net::SocketAddr]
///     - `associate`(optional) set whether the function to call need argument `Self`,
///        if set `associate` the signature of function to call is `Fn()->$ty`
///
/// ### logger
/// - using `logger(error="...", func="...",associate)` to impl `LoggerInitialization`,
/// the `func` and `associate` is similar to the `path` and `associate` of `address(func(path="...", associate))` but the return type became `Result<(),$error>`
///     - `error` the error that might ocurred during initialization the log system
///
/// ### server
/// - using `server="..."` to impl `ConfigureServerEffect` with internally call the provide func or
/// just using `server` or ignore it to having an empty implement. The function look like `fn (&self, Builder<AddrIncome>) -> Builder<AddrIncome>`
///
#[proc_macro_derive(Configure, attributes(conf))]
pub fn derive_config_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let derive_input = parse_macro_input!(input as DeriveInput);
    darling_err!(derive_config::provider_derive(derive_input))
}

/// impl `FromStateCollector` for special type
///
/// this implement is easy but boring, thus need macro to simplify it
#[proc_macro_derive(FromStateCollector)]
pub fn derive_from_state_collector(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let derive_input = parse_macro_input!(input as DeriveInput);
    darling_err!(from_state_collector::from_state_collector_macro(
        derive_input
    ))
}

/// make a function can apply as a `Prepare`
///
/// ## Example
///
/// [macro@prepare] support either sync or async function.
/// It can generate type which implement the [`Prepare`](https://docs.rs/axum-starter/latest/axum_starter/trait.Prepare.html) trait
///
/// the function arguments require can be provide by  the `Configure`.
///
/// the return type , usually can be one of :
/// - `()`
/// - `Result<impl @, CustomError>`
/// - `impl @`
/// > the `@` can be `PrepareRouteEffect`,
/// `PrepareStateEffect` or
///  `PrepareMiddlewareEffect`
///
/// **Note** if the return type is `Result<impl @, Error>`, need add `?` following the
/// generate Name
///
/// ```rust
/// #[prepare(Foo?)]
/// fn prepare_foo() -> Result<(), std::io::Error>{
///     // do something that might return Err()
///     todo!()
/// }
/// ```
///
/// the generate type name is present by the macro argument, for example, if you want a Prepare task
/// named `SeaConn`, just using like `#[prepare(SeaConn)]`
///
/// if your function argument contain reference or other types witch need a lifetime, just add the lifetime to the macro arguments list,
/// like this.
///
/// ```rust
/// #[prepare(Foo 'f)]
/// fn prepare_foo(foo_name: &'f String){
///     // do somethings
/// }
/// ```
/// Or,you can not provide any lifetime symbol, the macro will automatic find all needing lifetime places and giving a default symbol
///
/// ```rust
/// #[prepare(Foo)]
/// fn prepare_foo(foo_name: &String){
///     // do somethings
/// }
/// ```
///
/// Or only give lifetime symbol in macro input. The macro will auto replace `'_` into `'arg` if necessary
///
/// ```rust
/// #[prepare(Foo 'arg)]
/// fn prepare_foo(foo_name: &String){
///     // do somethings
/// }
/// ```
///
/// some times store `Future` on stack may cause ***Stack Overflow***, you can using `box` before generate name
/// make the return type became `Pin<Box<dyn Future>>`
///
/// ```rust
/// #[prepare(box Foo)]
/// async fn prepare_foo(){
///     // do something may take place large space
/// }
/// ```
///
/// if you want a `Prepare` return `Ready`, or in other word, a sync `Prepare`,you can use `sync` before the Ident.
/// note that `box` and `sync` cannot use in the same time
///
/// ```rust
/// #[prepare(sync Foo)]
/// fn prepare_foo(){
///     // do something not using `await`
/// }
/// ```
///
/// By default, the macro will not keep the origin function exist, if you want use that original function, using `origin`,
/// the `origin` is after the `box` or `sync`, but before the Ident
///
///```rust
/// #[prepare(sync origin Foo)]
/// fn prepare_foo(){
///     // do something not using `await`
/// }
/// ```
///```
#[proc_macro_attribute]
pub fn prepare(
    attrs: proc_macro::TokenStream,
    input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let prepare_name = parse_macro_input!(attrs as PrepareName);
    let fn_item = parse_macro_input!(input as ItemFn);

    match prepare_macro::prepare_macro(&prepare_name, fn_item) {
        Ok(token) => token,
        Err(error) => error.to_compile_error().into(),
    }
}