1#![recursion_limit="256"]
2
3extern crate proc_macro;
4
5use quote::quote;
6use darling::FromDeriveInput;
7
8#[derive(Debug, FromDeriveInput)]
9#[darling(attributes(validate))]
10struct JsonBodyOpts {
11 #[darling(default)]
12 context: Option<syn::Path>,
13}
14
15#[proc_macro_derive(JsonBody, attributes(validate))]
40pub fn derive_jsonbody(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
41 let ast = syn::parse_macro_input!(input as syn::DeriveInput);
42 let name = &ast.ident;
43
44 let opts = JsonBodyOpts::from_derive_input(&ast).unwrap();
45
46 let (ctx_line, validate_call) = if let Some(ctx) = opts.context {
47 (
48 quote! {
50 let ctx: & #ctx = req.rocket().state::<#ctx>().expect("context state not found");
51 },
52 quote! { obj.validate_with_args(ctx) }
54 )
55 } else {
56 (
57 quote! {},
59 quote! { obj.validate() }
61 )
62 };
63
64 let gen = quote! {
65 #[rocket::async_trait]
66 impl<'r> rocket::data::FromData<'r> for #name {
67 type Error = ();
68
69 async fn from_data(
70 req: &'r rocket::Request<'_>,
71 data: rocket::data::Data<'r>
72 ) -> rocket::data::Outcome<'r, Self> {
73 if req.content_type() != Some(&rocket::http::ContentType::new("application", "json")) {
75 return rocket::data::Outcome::Forward((data, rocket::http::Status::Continue));
76 }
77
78 let json_outcome = rocket::serde::json::Json::<Self>::from_data(req, data).await;
80
81 let obj = match json_outcome {
82 rocket::data::Outcome::Success(json) => json.into_inner(),
83 rocket::data::Outcome::Error(_) => {
84 req.local_cache(|| rocketjson::error::JsonBodyError::JsonValidationError);
85 return rocket::data::Outcome::Error((rocket::http::Status::BadRequest, ()));
86 },
87 rocket::data::Outcome::Forward(f) => return rocket::data::Outcome::Forward(f),
88 };
89
90 #ctx_line
92
93 if let Err(errors) = #validate_call {
94 req.local_cache(|| rocketjson::error::JsonBodyError::ValidationError(errors));
95 return rocket::data::Outcome::Error((rocket::http::Status::BadRequest, ()));
96 }
97
98 rocket::data::Outcome::Success(obj)
99 }
100 }
101 };
102
103 gen.into()
104}