serde_double_tag_derive/
lib.rs

1//! This crate contains proc-macros for the [`serde-double-tag`] crate.
2//!
3//! See the documentation of that crate for more information.
4
5mod generate;
6mod input;
7mod util;
8
9fn crate_name() -> syn::Path {
10	let mut segments = syn::punctuated::Punctuated::new();
11	segments.push(syn::PathSegment {
12		ident: syn::Ident::new("serde_double_tag", proc_macro2::Span::call_site()),
13		arguments: syn::PathArguments::None,
14	});
15	syn::Path {
16		leading_colon: Some(syn::token::PathSep(proc_macro2::Span::call_site())),
17		segments,
18	}
19}
20
21struct Context {
22	internal: syn::Path,
23	serde: syn::Path,
24	#[cfg(feature = "schemars")]
25	schemars: syn::Path,
26	errors: Vec<syn::Error>,
27}
28
29impl Context {
30	fn new(crate_name: syn::Path) -> Self {
31		let internal = extend_path(&crate_name, "internal__");
32		let serde = extend_path(&internal, "serde");
33		#[cfg(feature = "schemars")]
34		let schemars = extend_path(&internal, "schemars");
35		Self {
36			internal,
37			serde,
38			#[cfg(feature = "schemars")]
39			schemars,
40			errors: Vec::new(),
41		}
42	}
43
44	fn error(&mut self, span: proc_macro2::Span, message: impl std::fmt::Display) {
45		self.errors.push(syn::Error::new(span, format_args!("serde_double_tag: {message}")))
46	}
47
48	fn spanned_error<T: quote::ToTokens>(&mut self, object: &T, message: impl std::fmt::Display) {
49		self.errors.push(syn::Error::new_spanned(
50			object,
51			format_args!("serde_double_tag: {message}"),
52		))
53	}
54
55	fn syn_error(&mut self, error: syn::Error) {
56		self.error(error.span(), error)
57	}
58
59	fn collect_errors(self, mut tokens: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
60		for error in self.errors {
61			tokens.extend(error.into_compile_error())
62		}
63		tokens
64	}
65}
66
67#[proc_macro_derive(Deserialize, attributes(serde))]
68pub fn derive_deserialize(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
69	let mut context = Context::new(crate_name());
70	let output = match input::Enum::parse2(&mut context, tokens.into()) {
71		Ok(input) => generate::impl_deserialize_enum(&mut context, input),
72		Err(()) => proc_macro2::TokenStream::new(),
73	};
74	context.collect_errors(output).into()
75}
76
77#[proc_macro_derive(Serialize, attributes(serde))]
78pub fn derive_serialize(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
79	let mut context = Context::new(crate_name());
80	let output = match input::Enum::parse2(&mut context, tokens.into()) {
81		Ok(input) => generate::impl_serialize_enum(&mut context, input),
82		Err(()) => proc_macro2::TokenStream::new(),
83	};
84	context.collect_errors(output).into()
85}
86
87#[proc_macro_derive(JsonSchema, attributes(serde))]
88#[cfg(feature = "schemars")]
89pub fn derive_json_schema(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
90	let mut context = Context::new(crate_name());
91	let output = match input::Enum::parse2(&mut context, tokens.into()) {
92		Ok(input) => generate::impl_json_schema(&mut context, input),
93		Err(()) => proc_macro2::TokenStream::new(),
94	};
95	context.collect_errors(output).into()
96}
97
98fn extend_path(path: &syn::Path, segment: &str) -> syn::Path {
99	let mut output = path.clone();
100	output.segments.push(syn::PathSegment {
101		ident: syn::Ident::new(segment, proc_macro2::Span::call_site()),
102		arguments: Default::default(),
103	});
104	output
105}