Skip to main content

sark_gen/
lib.rs

1//! `#[sark_gen::json]` derives compact JSON encode/decode for named structs.
2//! Fields default to scalars (`u64`, `bool`, `Bytes<Retained>`, `Option<T>`). Two
3//! `#[field(...)]` attributes compose nested documents:
4//!
5//! - `#[field(nested)]` on a field whose type is another `#[sark_gen::json]` struct
6//!   emits `"key":{...}` using that type's own encoder.
7//! - `#[field(seq)]` on a `Vec<Bytes<Retained>>` emits a JSON string array
8//!   `"key":["a","b"]`; combine with `nested` (`#[field(seq, nested)]`) on a
9//!   `Vec<T>` of json structs to emit an array of objects `"key":[{...},{...}]`.
10//!
11//! Empty vectors encode as `[]`. `nested`/`seq` are not supported with `exact`.
12//!
13//! ```ignore
14//! #[sark_gen::json(ordered)]
15//! struct Rating { score: u64, count: u64 }
16//!
17//! #[sark_gen::json(ordered)]
18//! struct Item {
19//!     id: u64,
20//!     #[field(seq)] tags: Vec<Bytes<Retained>>,
21//!     #[field(nested)] rating: Rating,
22//! }
23//!
24//! #[sark_gen::json(ordered)]
25//! struct ItemsResponse {
26//!     #[field(seq, nested)] items: Vec<Item>,
27//!     count: u64,
28//! }
29//! ```
30
31use proc_macro::TokenStream;
32use syn::{Item, ItemStruct, parse_macro_input};
33
34mod app;
35mod body_input;
36mod codegen;
37mod define_route_input;
38mod handler;
39mod json;
40mod lifetimes;
41mod model;
42mod request;
43mod response;
44mod route_compiler;
45mod util;
46
47#[proc_macro]
48pub fn body(input: TokenStream) -> TokenStream {
49    let input = parse_macro_input!(input as body_input::BodyInput);
50    input.expand().into()
51}
52
53fn emit(result: syn::Result<proc_macro2::TokenStream>) -> TokenStream {
54    match result {
55        Ok(tokens) => tokens.into(),
56        Err(error) => error.into_compile_error().into(),
57    }
58}
59
60#[proc_macro]
61pub fn define_route(input: TokenStream) -> TokenStream {
62    let input = parse_macro_input!(input as define_route_input::DefineRouteInput);
63    emit(app::define_route(input))
64}
65
66#[proc_macro_attribute]
67pub fn handler(attr_args: TokenStream, item: TokenStream) -> TokenStream {
68    if !attr_args.is_empty() {
69        return syn::Error::new(
70            proc_macro2::Span::call_site(),
71            "#[sark_gen::handler] takes no arguments; path and method belong to define_route!",
72        )
73        .to_compile_error()
74        .into();
75    }
76    let item = parse_macro_input!(item as Item);
77    emit(match item {
78        Item::Fn(fun) => handler::Handler::new(fun).and_then(handler::Handler::expand),
79        other => Err(syn::Error::new_spanned(
80            other,
81            "#[sark_gen::handler] supports only fn",
82        )),
83    })
84}
85
86#[proc_macro_attribute]
87pub fn json(attr: TokenStream, item: TokenStream) -> TokenStream {
88    let st = parse_macro_input!(item as ItemStruct);
89    let mode = parse_macro_input!(attr as json::JsonMode);
90    emit(mode.expand(st))
91}
92
93#[proc_macro_attribute]
94pub fn request(attr: TokenStream, item: TokenStream) -> TokenStream {
95    let mode = parse_macro_input!(attr as request::Mode);
96    let st = parse_macro_input!(item as ItemStruct);
97    emit(mode.expand(st))
98}
99
100#[proc_macro_attribute]
101pub fn response(attr: TokenStream, item: TokenStream) -> TokenStream {
102    let mode = parse_macro_input!(attr as response::Mode);
103    let st = parse_macro_input!(item as ItemStruct);
104    emit(mode.expand(st))
105}