expect_json_macros/
lib.rs

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