afast_macros/
lib.rs

1//! # AFast
2//! 
3//! **AFast** is a high-performance asynchronous Rust backend framework designed
4//! to simplify building networked applications. It supports multiple protocols
5//! via feature flags and provides automatic code generation for clients
6//! (TypeScript and JavaScript), API documentation, and field validation.
7//! 
8//! ## Instructions
9//! 
10//! ### Supported Protocol Features
11//! 
12//! You can enable the following features in your `Cargo.toml`:
13//! 
14//! - `http` - enable HTTP support
15//!   - `/` - Document path (feature flag `doc`)
16//!   - `/api` - HTTP API endpoints
17//!   - `/code/{service}/{lang}` - Client code (feature flag `js`|`ts` ...)
18//!   - `/doc` - Service list (feature flag `doc`)
19//!   - `/doc/{service}` - Handler defintions and documentation (feature flag `doc`)
20//! - `ws` - enable WebSocket support
21//!   - `/ws` - WebSocket endpoint
22//! - `tcp` - enable TCP support
23//! - `doc` - enable API documentation generation
24//! - `js` - enable JavaScript client generation (auto enabled `code`)
25//! - `ts` - enable TypeScript client generation (auto enabled `code`)
26//! - `code` - enable code generation
27//! 
28//! **Note on TCP usage:**  
29//! 
30//! If the `tcp` feature is enabled, the `AFast::serve` method takes two arguments:
31//! 
32//! 1. The TCP address to listen on (`"127.0.0.1:8080"`).  
33//! 2. The HTTP/WS address (`"127.0.0.1:8081"`) for web clients and generated JS/TS clients.
34//! 
35//! This allows you to run TCP and HTTP/WS servers simultaneously in the same application.
36//! 
37//! ### Key Features
38//! 
39//! - **`handler` Macro**: Declare HTTP endpoints with minimal boilerplate
40//!   - Automatic TypeScript/JavaScript client generation
41//!   - Namespace support for organized API structure (`ns("api.v1.user")`)
42//!   - Descriptive API documentation generation (`desc("Get user info")`)
43//! - Automatic field validation with custom rules
44//! - Async handler functions with state management
45//! - Flexible multi-protocol support: HTTP, WS, TCP
46//! 
47//! #### Handler Macro Overview
48//! 
49//! The `#[handler]` attribute macro transforms async functions into full-featured API endpoints:
50//! 
51//! ```rust
52//! #[handler(desc("Get user information"), ns("api.v1.user"))]
53//! async fn get_user(state: String, header: Header, req: Request) -> Result<Response, Error> {
54//!     // Your business logic
55//! }
56//! ```
57//! 
58//! **Macro Parameters:**
59//! 
60//! - `desc("description")` - API description for documentation
61//! - `ns("api.v1.user")` - Namespace for nested JS client generation
62//! 
63//! **Generated Output:**
64//! 
65//! - Type-safe HTTP endpoints
66//! - Nested JavaScript client structure
67//! - TypeScript type definitions  
68//! - OpenAPI documentation
69//! 
70//! ### Upcoming Features / Development Plan
71//! 
72//! - Nested structure validation for complex types
73//! - Enable or disable js / ts / document by feature flags
74//! - Add command for generating client code
75//! - Generate client code for additional languages: Java, Kotlin, C#, Rust, etc.
76//! - Improved code generation templates for easier integration
77//! - Enhanced error handling and validation reporting
78//! 
79//! ## Example
80//! 
81//! ```rust
82//! use afast::{AFast, AFastData, AFastKind, Error, handler, middleware, register};
83//! 
84//! #[derive(Debug, Clone, AFastData, AFastKind)]
85//! enum Sex {
86//!     Other,
87//!     Custom(#[validate(desc("Custom user sex 0"))] i32, String),
88//!     Male {
89//!         #[validate(desc("Male user id"))]
90//!         id: i64,
91//!     },
92//!     Female {
93//!         #[validate(desc("Female user name"))]
94//!         name: String,
95//!     },
96//! }
97//! 
98//! #[derive(Debug, Clone, AFastData, AFastKind)]
99//! struct Request {
100//!     #[validate(desc("User ID"))]
101//!     id: i64,
102//!     #[validate(desc("User name"))]
103//!     name: String,
104//!     #[validate(
105//!         desc("User age"),
106//!         required("age is required"),
107//!         min(1, "age must be at least 1"),
108//!         max(256, "age must be at most 256")
109//!     )]
110//!     age: u32,
111//!     #[validate(desc("User hobbies"))]
112//!     hobbies: Vec<Hobby>,
113//!     #[validate(desc("User tags"))]
114//!     tags: Vec<String>,
115//!     #[validate(desc("User gender"))]
116//!     gender: Option<bool>,
117//!     #[validate(desc("User sex"))]
118//!     sex: Sex,
119//! }
120//! 
121//! #[derive(Debug, Clone, AFastData, AFastKind)]
122//! struct Hobby {
123//!     id: i64,
124//!     name: String,
125//! }
126//! 
127//! #[derive(Debug, AFastData, AFastKind)]
128//! pub struct Response {
129//!     sex: Sex,
130//!     id: i64,
131//!     name: String,
132//!     age: u32,
133//!     hobbies: Vec<Hobby>,
134//!     tags: Vec<String>,
135//!     gender: Option<bool>,
136//! }
137//! 
138//! #[handler(desc("Get user information"), ns("api.user"))]
139//! async fn get_user(_state: String, _header: Header, req: Request) -> Result<Response, Error> {
140//!     Ok(Response {
141//!         id: req.id,
142//!         name: req.name.clone(),
143//!         age: req.age,
144//!         hobbies: req.hobbies.clone(),
145//!         tags: req.tags.clone(),
146//!         gender: req.gender,
147//!         sex: req.sex.clone(),
148//!     })
149//! }
150//! 
151//! #[derive(Debug, AFastData, AFastKind)]
152//! struct Req2 {
153//!     id: i64,
154//! }
155//! 
156//! #[derive(Debug, AFastData, AFastKind)]
157//! struct Resp2 {
158//!     id: i64,
159//!     name: String,
160//! }
161//! 
162//! #[handler(desc("Get user by id"), ns("api"))]
163//! async fn get_id(_state: String, _header: Header, req: Req2) -> Result<Resp2, Error> {
164//!     Ok(Resp2 {
165//!         id: req.id,
166//!         name: "John".to_string(),
167//!     })
168//! }
169//! 
170//! #[derive(Debug, Clone, AFastData, AFastKind)]
171//! struct Header {
172//!     token: String,
173//! }
174//! 
175//! #[middleware]
176//! async fn auth(_state: String, header: Header) -> Result<Header, Error> {
177//!     println!("Token: {:?}", header);
178//!     Ok(header)
179//! }
180//! 
181//! #[tokio::main]
182//! async fn main() {
183//!     let state = "".to_string();
184//! 
185//!     let server = AFast::<String, Header>::new(state)
186//!         .service("user", "User service", register! { get_user, get_id })
187//!         .middleware(auth);
188//! 
189//!     server
190//!         .serve(
191//!             #[cfg(feature = "tcp")]
192//!             &"127.0.0.1:8080",
193//!             #[cfg(any(feature = "http", feature = "ws"))]
194//!             &"127.0.0.1:8081",
195//!         )
196//!         .await
197//!         .unwrap();
198//! }
199//! ```
200//! 
201
202use proc_macro::TokenStream;
203
204mod atd;
205mod deserialize;
206mod handler;
207mod serialize;
208
209use quote::quote;
210use syn::{
211    Attribute, Data, DeriveInput, FnArg, ItemFn, LitInt, LitStr, Meta, PatType, Token,
212    parse::{Parse, ParseStream},
213    parse_macro_input,
214    punctuated::Punctuated,
215    spanned::Spanned as _,
216};
217
218/// Attribute macro to define an API handler.
219#[proc_macro_attribute]
220pub fn handler(attr: TokenStream, item: TokenStream) -> TokenStream {
221    atd::handler(attr, item)
222}
223
224/// Macro to register multiple handlers.
225#[proc_macro]
226pub fn register(input: TokenStream) -> TokenStream {
227    atd::register(input)
228}
229
230#[proc_macro_attribute]
231pub fn middleware(_attr: TokenStream, input: TokenStream) -> TokenStream {
232    let input = parse_macro_input!(input as ItemFn);
233
234    let ident = &input.sig.ident;
235    let vis = &input.vis;
236    let block = &input.block;
237    let sig = &input.sig;
238    let mut state_ty = None;
239    let mut header_ty = None;
240    for (i, arg) in sig.inputs.iter().enumerate() {
241        if let FnArg::Typed(PatType { ty, .. }) = arg {
242            match i {
243                0 => state_ty = Some(ty.clone()),
244                1 => header_ty = Some(ty.clone()),
245                _ => {}
246            }
247        }
248    }
249
250    let state_ty = state_ty.expect("Expected first parameter to be state");
251    let header_ty = header_ty.expect("Expected second parameter to be header");
252
253    TokenStream::from(quote! {
254        #vis fn #ident() -> Box<afast::Middleware<#state_ty, #header_ty>> {
255            Box::new(|state, header| Box::pin(async move {
256                #block
257            }))
258        }
259    })
260}
261
262/// Derive macro to generate serialization, deserialization, and validation implementations.
263#[proc_macro_derive(AFastData, attributes(validate, afast))]
264pub fn serialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
265    let input = parse_macro_input!(input as DeriveInput);
266    let name = input.ident;
267    let mut code = Vec::new();
268
269    let (serialize_code, deserialize_code, validate_code) = match &input.data {
270        Data::Struct(data) => match handler::rust::handler_struct(&name, data) {
271            Ok(ts) => ts,
272            Err(e) => return e.to_compile_error().into(),
273        },
274        Data::Enum(data) => match handler::rust::handler_enum(&name, data) {
275            Ok(ts) => ts,
276            Err(e) => return e.to_compile_error().into(),
277        },
278        Data::Union(_data) => panic!("unions are not supported yet"),
279    };
280
281    code.push(quote! {
282        fn to_bytes(&self) -> Vec<u8> {
283            let mut buf = Vec::new();
284            #(#serialize_code)*
285            buf
286        }
287
288        fn from_bytes(buf: &[u8]) -> Result<(Self, usize), ::afast::Error> {
289            let _length = buf.len();
290            let mut _offset = 0;
291            #(#deserialize_code)*
292        }
293
294        fn validate(&self) -> Result<(), Vec<&'static str>> {
295            #(#validate_code)*
296            Ok(())
297        }
298    });
299
300    let expanded = quote! {
301        impl ::afast::AFastData for #name {
302            #(#code)*
303        }
304    };
305
306    TokenStream::from(expanded)
307}
308
309#[derive(Debug)]
310struct Tag {
311    name: Option<String>,
312    description: Option<String>,
313    required: Option<String>,
314    min: Option<(i64, String)>,
315    max: Option<(i64, String)>,
316}
317
318struct IntStrTuple {
319    int: LitInt,
320    _comma: Token![,],
321    msg: LitStr,
322}
323
324impl Parse for IntStrTuple {
325    fn parse(input: ParseStream) -> syn::Result<Self> {
326        Ok(IntStrTuple {
327            int: input.parse()?,
328            _comma: input.parse()?,
329            msg: input.parse()?,
330        })
331    }
332}
333
334/// Derive macro to generate type metadata (kind information).
335#[proc_macro_derive(AFastKind, attributes(validate, afast))]
336pub fn derive_get_kind(input: TokenStream) -> TokenStream {
337    let input = parse_macro_input!(input as DeriveInput);
338    let name = &input.ident;
339    let generics = &input.generics;
340
341    let kind_impl = match generate_kind_expression(&input.data) {
342        Ok(kind_impl) => kind_impl,
343        Err(err) => return err.to_compile_error().into(),
344    };
345
346    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
347
348    let expanded = quote! {
349        impl #impl_generics AFastKind for #name #ty_generics #where_clause {
350            fn kind() -> afast::Kind {
351                #kind_impl
352            }
353
354            fn field(name: &str) -> afast::Field {
355                afast::Field { name: name.to_string(), kind: <#name as AFastKind>::kind(), tag: None }
356            }
357        }
358    };
359
360    TokenStream::from(expanded)
361}
362
363/// Generates `Kind` expression for struct/enum data.
364fn generate_kind_expression(data: &Data) -> Result<proc_macro2::TokenStream, syn::Error> {
365    match data {
366        Data::Struct(data_struct) => match &data_struct.fields {
367            syn::Fields::Named(fields) => {
368                let mut field_kinds = Vec::new();
369                for f in &fields.named {
370                    if parse_ignore(&f.attrs)? {
371                        continue;
372                    }
373                    let field_name = f.ident.as_ref().unwrap();
374                    let field_type = &f.ty;
375                    let kind_expr = type_to_kind_expr(field_type);
376                    let field_tag = parse_tags(&f.attrs)?;
377                    let tag_expr = generate_tag_expression(&field_tag);
378
379                    field_kinds.push(quote! {
380                        afast::Field { name: stringify!(#field_name).to_string(), kind: #kind_expr, tag: #tag_expr }
381                    });
382                }
383
384                Ok(quote! { afast::Kind::Struct { fields: vec![#(#field_kinds),*] } })
385            }
386            syn::Fields::Unnamed(fields) => {
387                let mut field_kinds = Vec::new();
388                for (i, f) in fields.unnamed.iter().enumerate() {
389                    if parse_ignore(&f.attrs)? {
390                        continue;
391                    }
392                    let field_type = &f.ty;
393                    let kind_expr = type_to_kind_expr(field_type);
394                    let field_tag = parse_tags(&f.attrs)?;
395                    let tag_expr = generate_tag_expression(&field_tag);
396
397                    field_kinds.push(quote! {
398                        afast::Field { name: format!("_{}", #i), kind: #kind_expr, tag: #tag_expr }
399                    });
400                }
401                Ok(quote! { afast::Kind::Struct { fields: vec![#(#field_kinds),*] } })
402            }
403            syn::Fields::Unit => Ok(quote! { afast::Kind::Struct { fields: vec![] } }),
404        },
405        Data::Enum(data_enum) => {
406            let mut variant_kinds = Vec::new();
407            for variant in &data_enum.variants {
408                let name = &variant.ident.to_string();
409                let variant_kind = match &variant.fields {
410                    syn::Fields::Unit => quote! { (afast::Kind::Unit, #name.to_string()) },
411                    syn::Fields::Unnamed(fields) => {
412                        let mut field_kinds = Vec::new();
413                        for (i, f) in fields.unnamed.iter().enumerate() {
414                            if parse_ignore(&f.attrs)? {
415                                continue;
416                            }
417                            let field_type = &f.ty;
418                            let kind_expr = type_to_kind_expr(field_type);
419                            let field_tag = parse_tags(&f.attrs)?;
420                            let tag_expr = generate_tag_expression(&field_tag);
421
422                            field_kinds.push(quote! { afast::Field { name: format!("_{}", #i), kind: #kind_expr, tag: #tag_expr } });
423                        }
424                        quote! { (afast::Kind::Struct { fields: vec![#(#field_kinds),*] }, #name.to_string()) }
425                    }
426                    syn::Fields::Named(fields) => {
427                        let mut field_kinds = Vec::new();
428                        for f in &fields.named {
429                            if parse_ignore(&f.attrs)? {
430                                continue;
431                            }
432                            let field_name = f.ident.as_ref().unwrap();
433                            let field_type = &f.ty;
434                            let kind_expr = type_to_kind_expr(field_type);
435                            let field_tag = parse_tags(&f.attrs)?;
436                            let tag_expr = generate_tag_expression(&field_tag);
437
438                            field_kinds.push(quote! { afast::Field { name: stringify!(#field_name).to_string(), kind: #kind_expr, tag: #tag_expr } });
439                        }
440                        quote! { (afast::Kind::Struct { fields: vec![#(#field_kinds),*] }, #name.to_string()) }
441                    }
442                };
443                variant_kinds.push(variant_kind);
444            }
445            Ok(quote! { afast::Kind::Enum { variants: vec![#(#variant_kinds),*] } })
446        }
447        Data::Union(_) => Ok(quote! { afast::Kind::Struct { fields: vec![] } }),
448    }
449}
450
451/// Generates optional `Tag` metadata expression.
452fn generate_tag_expression(tag: &Tag) -> proc_macro2::TokenStream {
453    let name = tag
454        .name
455        .as_ref()
456        .map(|n| quote! { Some(#n.to_string()) })
457        .unwrap_or(quote! { None });
458    let description = tag
459        .description
460        .as_ref()
461        .map(|d| quote! { Some(#d.to_string()) })
462        .unwrap_or(quote! { None });
463    let required = tag
464        .required
465        .as_ref()
466        .map(|r| quote! { Some(#r.to_string()) })
467        .unwrap_or(quote! { None });
468    let min = tag
469        .min
470        .as_ref()
471        .map(|(v, msg)| quote! { Some((#v, #msg.to_string())) })
472        .unwrap_or(quote! { None });
473    let max = tag
474        .max
475        .as_ref()
476        .map(|(v, msg)| quote! { Some((#v, #msg.to_string())) })
477        .unwrap_or(quote! { None });
478
479    if tag.name.is_none()
480        && tag.description.is_none()
481        && tag.required.is_none()
482        && tag.min.is_none()
483        && tag.max.is_none()
484    {
485        quote! { None }
486    } else {
487        quote! { Some(afast::Tag { name: #name, description: #description, required: #required, min: #min, max: #max }) }
488    }
489}
490
491/// Parses field attributes for validation tags.
492fn parse_tags(attrs: &Vec<Attribute>) -> Result<Tag, syn::Error> {
493    let mut tag = Tag {
494        name: None,
495        description: None,
496        required: None,
497        min: None,
498        max: None,
499    };
500    for attr in attrs {
501        if attr.path().is_ident("validate") {
502            let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
503            for meta in nested {
504                match meta {
505                    Meta::List(meta) => {
506                        if meta.path.is_ident("name") {
507                            tag.name = Some(meta.parse_args::<LitStr>()?.value());
508                        } else if meta.path.is_ident("desc") {
509                            tag.description = Some(meta.parse_args::<LitStr>()?.value());
510                        } else if meta.path.is_ident("required") {
511                            tag.required = Some(meta.parse_args::<LitStr>()?.value());
512                        } else if meta.path.is_ident("min") {
513                            let inner = meta.parse_args::<IntStrTuple>()?;
514                            tag.min = Some((inner.int.base10_parse::<i64>()?, inner.msg.value()));
515                        } else if meta.path.is_ident("max") {
516                            let inner = meta.parse_args::<IntStrTuple>()?;
517                            tag.max = Some((inner.int.base10_parse::<i64>()?, inner.msg.value()));
518                        } else {
519                            return Err(syn::Error::new(
520                                meta.path.span(),
521                                format!(
522                                    "Unknown validate attribute: {}",
523                                    meta.path.get_ident().unwrap()
524                                ),
525                            ));
526                        }
527                    }
528                    _ => {}
529                }
530            }
531        }
532    }
533    Ok(tag)
534}
535
536/// Converts Rust types to `Kind` expressions for metadata.
537fn type_to_kind_expr(ty: &syn::Type) -> proc_macro2::TokenStream {
538    match ty {
539        syn::Type::Path(type_path) => {
540            if let Some(segment) = type_path.path.segments.last() {
541                let type_name = segment.ident.to_string();
542                if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
543                    if type_name == "Option" {
544                        if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
545                            let inner_kind = type_to_kind_expr(inner_ty);
546                            return quote! { afast::Kind::Nullable(Box::new(#inner_kind)) };
547                        }
548                    } else if type_name == "Vec" {
549                        if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
550                            let inner_kind = type_to_kind_expr(inner_ty);
551                            return quote! { afast::Kind::Vec(Box::new(#inner_kind)) };
552                        }
553                    }
554                }
555                match type_name.as_str() {
556                    "i8" => quote! { afast::Kind::I8 },
557                    "i16" => quote! { afast::Kind::I16 },
558                    "i32" => quote! { afast::Kind::I32 },
559                    "i64" => quote! { afast::Kind::I64 },
560                    "i128" => quote! { afast::Kind::I128 },
561                    "u8" => quote! { afast::Kind::U8 },
562                    "u16" => quote! { afast::Kind::U16 },
563                    "u32" => quote! { afast::Kind::U32 },
564                    "u64" => quote! { afast::Kind::U64 },
565                    "u128" => quote! { afast::Kind::U128 },
566                    "f32" => quote! { afast::Kind::F32 },
567                    "f64" => quote! { afast::Kind::F64 },
568                    "bool" => quote! { afast::Kind::Bool },
569                    "String" => quote! { afast::Kind::String },
570                    "str" => quote! { afast::Kind::String },
571                    "()" => quote! { afast::Kind::Unit },
572                    _ => quote! { <#ty as AFastKind>::kind() },
573                }
574            } else {
575                quote! { afast::Kind::String }
576            }
577        }
578        syn::Type::Tuple(type_tuple) => {
579            if type_tuple.elems.is_empty() {
580                quote! { afast::Kind::Unit }
581            } else {
582                quote! { afast::Kind::String }
583            }
584        }
585        _ => quote! { afast::Kind::String },
586    }
587}
588
589fn parse_ignore(attrs: &Vec<Attribute>) -> Result<bool, syn::Error> {
590    for attr in attrs {
591        if attr.path().is_ident("afast") {
592            let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
593            for meta in nested {
594                match meta {
595                    Meta::Path(path) if path.is_ident("ignore") => return Ok(true),
596                    _ => {}
597                }
598            }
599        }
600    }
601    Ok(false)
602}