expect_json_macros/
lib.rs1use 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}