expect_json_macros/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::quote;
4use syn::parse_macro_input;
5
6mod args;
7use args::*;
8
9#[proc_macro_attribute]
10pub fn expect_op(args: TokenStream, input: TokenStream) -> TokenStream {
11    let args = parse_macro_input!(args as Args);
12
13    expect_op_impl(args, input)
14}
15
16#[doc(hidden)]
17#[proc_macro_attribute]
18pub fn expect_op_for_axum_test(args: TokenStream, input: TokenStream) -> TokenStream {
19    let mut args = parse_macro_input!(args as Args);
20    if args.crate_name_str != "expect_json" {
21        panic!("expect_op does not support internal for axum test");
22    }
23
24    args.crate_name_str = "::axum_test::expect_json";
25    args.crate_name = quote!(::axum_test::expect_json);
26
27    expect_op_impl(args, input)
28}
29
30fn expect_op_impl(args: Args, input: TokenStream) -> TokenStream {
31    let crate_name_str = args.crate_name_str;
32    let crate_name = args.crate_name;
33
34    let input_tokens: TokenStream2 = input.clone().into();
35    let input_item = syn::parse_macro_input!(input as syn::Item);
36    let struct_name = match input_item {
37        syn::Item::Struct(item_struct) => item_struct.ident,
38        syn::Item::Enum(item_enum) => item_enum.ident,
39        _ => panic!("expect_op can only be used on structs or enums"),
40    };
41    let struct_name_str = args.display_name.unwrap_or_else(|| struct_name.to_string());
42    let serde_trampoline_path = format!("{crate_name_str}::__private::serde_trampoline");
43
44    let output = quote! {
45        #[derive(#crate_name::__private::serde::Serialize, #crate_name::__private::serde::Deserialize)]
46        #[serde(crate = #serde_trampoline_path)]
47        #input_tokens
48
49        impl #crate_name::__private::serde::Serialize for #struct_name {
50            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51            where
52                S: #crate_name::__private::serde::Serializer,
53            {
54                #crate_name::SerializeExpectOp::serialize(self, serializer)
55            }
56        }
57
58        use #crate_name::__private::typetag;
59        #[#crate_name::__private::typetag::serde]
60        impl #crate_name::ExpectOpSerialize for #struct_name {}
61
62        impl #crate_name::ExpectOpExt for #struct_name {
63            fn name(&self) -> &'static str {
64                #struct_name_str
65            }
66        }
67
68        impl From<#struct_name> for #crate_name::__private::serde_json::Value {
69            fn from(value: #struct_name) -> Self {
70                #crate_name::__private::serde_json::to_value(&value).unwrap()
71            }
72        }
73    };
74
75    output.into()
76}