1#![deny(unsafe_code)]
2
3#[cfg(feature = "rpc")]
4mod rpc;
5
6#[cfg(feature = "rpc")]
7use quote::quote;
8
9mod args;
10mod docs;
11mod impl_enum;
12mod impl_inventory;
13mod impl_namespace;
14mod impl_struct;
15
16use darling::{ast::Data, FromDeriveInput};
17use docs::RustDocs;
18use proc_macro::TokenStream;
19use proc_macro_error::proc_macro_error;
20use quote::{format_ident, quote_spanned};
21use serde_derive_internals::Derive;
22use syn::{Ident, Path};
23
24use proc_macro2::Span;
25
26fn get_crate_name() -> String {
27 let found_crate = proc_macro_crate::crate_name("zod").unwrap_or_else(|err| {
28 proc_macro_error::abort_call_site!("Error: {}", err);
29 });
30
31 match found_crate {
32 proc_macro_crate::FoundCrate::Itself => String::from("zod"),
33 proc_macro_crate::FoundCrate::Name(name) => name,
34 }
35}
36
37pub(crate) fn get_zod() -> Path {
38 let name = get_crate_name();
39 let ident = Ident::new(&name, Span::call_site());
40 syn::parse_quote!(::#ident)
41}
42
43pub(crate) fn get_zod_spanned(span: Span) -> Path {
44 let name = get_crate_name();
45 let ident = Ident::new(&name, Span::call_site());
46 syn::parse_quote_spanned!(span => ::#ident)
47}
48
49pub(crate) fn get_private() -> Path {
50 let zod = get_zod();
51 syn::parse_quote!(#zod::__private)
52}
53
54pub(crate) fn get_private_spanned(span: Span) -> Path {
55 let zod = get_zod_spanned(span);
56 syn::parse_quote_spanned!(span => #zod::__private)
57}
58
59#[proc_macro_error]
60#[proc_macro_derive(Zod, attributes(zod))]
61pub fn zod(input: TokenStream) -> TokenStream {
62 let parsed = match syn::parse(input) {
63 Ok(parsed) => parsed,
64 Err(err) => {
65 return err.into_compile_error().into();
66 }
67 };
68
69 let cx = serde_derive_internals::Ctxt::new();
70
71 let container =
72 serde_derive_internals::ast::Container::from_ast(&cx, &parsed, Derive::Deserialize)
73 .unwrap();
74
75 cx.check().unwrap();
76
77 let docs = match RustDocs::from_attrs(&parsed.attrs) {
78 Ok(docs) => docs,
79 Err(err) => {
80 return err.into_compile_error().into();
81 }
82 };
83
84 let input = match args::Input::from_derive_input(&parsed) {
85 Ok(input) => input,
86 Err(err) => {
87 return err.write_errors().into();
88 }
89 };
90
91 let expanded = match input.data.clone() {
92 Data::Enum(e) => impl_enum::expand(input, &e, container, docs),
93 Data::Struct(e) => impl_struct::expand(input, e, container, docs),
94 };
95 expanded.into()
96}
97
98#[proc_macro_error]
99#[proc_macro_derive(Namespace, attributes(namespace))]
100pub fn derive_namespace(input: TokenStream) -> TokenStream {
101 let parsed = match syn::parse(input) {
102 Ok(parsed) => parsed,
103 Err(err) => {
104 return err.into_compile_error().into();
105 }
106 };
107 let input = match args::NamespaceInput::from_derive_input(&parsed) {
108 Ok(input) => input,
109 Err(err) => {
110 return err.write_errors().into();
111 }
112 };
113
114 let docs = match RustDocs::from_attrs(&parsed.attrs) {
115 Ok(docs) => docs,
116 Err(err) => {
117 return err.into_compile_error().into();
118 }
119 };
120
121 impl_namespace::expand(input, docs).into()
122}
123
124#[cfg(feature = "rpc")]
125#[proc_macro_error]
126#[proc_macro_derive(Backend, attributes(rpc))]
127pub fn backend(input: TokenStream) -> TokenStream {
128 let parsed = match syn::parse(input) {
129 Ok(parsed) => parsed,
130 Err(err) => {
131 return err.into_compile_error().into();
132 }
133 };
134
135 let input = match rpc::args::BackendInput::from_derive_input(&parsed) {
136 Ok(input) => input,
137 Err(err) => {
138 return err.write_errors().into();
139 }
140 };
141
142 let expanded = match input.data.clone() {
143 Data::Enum(_) => unreachable!(),
144 Data::Struct(e) => rpc::backend_impl::expand(input, e),
145 };
146 expanded.into()
147}
148
149#[cfg(feature = "rpc")]
150#[proc_macro_error]
151#[proc_macro_attribute]
152pub fn namespace(_attr: TokenStream, input: TokenStream) -> TokenStream {
153 let orig = proc_macro2::TokenStream::from(input.clone());
154
155 let ast = syn::parse_macro_input!(input as syn::ItemImpl);
156 let extra = rpc::impl_rpc::expand(rpc::args::RpcInput::from_ast(ast));
157
158 let output = quote! {
159 #orig
160
161 #extra
162 };
163
164 output.into()
165}
166
167fn format_ident_for_registration(p: &syn::Path) -> syn::Path {
168 let mut segments = p.segments.clone();
169 let last = segments.last_mut().unwrap();
170 last.ident = format_ident!("__ZodRegister__{}", last.ident);
171
172 syn::Path {
173 leading_colon: p.leading_colon,
174 segments,
175 }
176}
177
178fn expand_type_registration(ident: &Ident, ns_path: &Path) -> proc_macro2::TokenStream {
180 let register_path = format_ident_for_registration(ns_path);
181
182 quote_spanned! {ident.span() =>
183 impl #register_path {
184 #[allow(dead_code)]
185 #[allow(non_upper_case_globals)]
186 const #ident: () = ();
187 }
188 }
189}