1use crate::lazy_bool::LazyBool;
2use darling::FromDeriveInput;
3use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::ToTokens;
6use std::ops::{BitAnd, BitOr, Not};
7use syn::{
8 parse::{Parse, ParseStream},
9 parse_quote,
10 punctuated::Punctuated,
11 Attribute, DeriveInput, Generics, Meta, Path, PathSegment, Token, TypeGenerics, WhereClause,
12};
13
14pub(crate) trait IteratorExt {
16 fn collect_error(self) -> syn::Result<()>
17 where
18 Self: Iterator<Item = syn::Result<()>> + Sized,
19 {
20 let accu = Ok(());
21 self.fold(accu, |accu, error| match (accu, error) {
22 (Ok(()), error) => error,
23 (accu, Ok(())) => accu,
24 (Err(mut err), Err(error)) => {
25 err.combine(error);
26 Err(err)
27 }
28 })
29 }
30}
31impl<I> IteratorExt for I where I: Iterator<Item = syn::Result<()>> + Sized {}
32
33#[derive(FromDeriveInput)]
35#[darling(attributes(serde_with))]
36pub(crate) struct DeriveOptions {
37 #[darling(rename = "crate", default)]
39 pub(crate) alt_crate_path: Option<Path>,
40}
41
42impl DeriveOptions {
43 pub(crate) fn from_derive_input(input: &DeriveInput) -> Result<Self, TokenStream> {
44 match <Self as FromDeriveInput>::from_derive_input(input) {
45 Ok(v) => Ok(v),
46 Err(e) => Err(TokenStream::from(e.write_errors())),
47 }
48 }
49
50 pub(crate) fn get_serde_with_path(&self) -> Path {
51 self.alt_crate_path
52 .clone()
53 .unwrap_or_else(|| syn::parse_str("::serde_with").unwrap())
54 }
55}
56
57pub(crate) fn split_with_de_lifetime(
60 generics: &Generics,
61) -> (DeImplGenerics<'_>, TypeGenerics<'_>, Option<&WhereClause>) {
62 let de_impl_generics = DeImplGenerics(generics);
63 let (_, ty_generics, where_clause) = generics.split_for_impl();
64 (de_impl_generics, ty_generics, where_clause)
65}
66
67pub(crate) struct DeImplGenerics<'a>(&'a Generics);
68
69impl ToTokens for DeImplGenerics<'_> {
70 fn to_tokens(&self, tokens: &mut TokenStream2) {
71 let mut generics = self.0.clone();
72 generics.params = Some(parse_quote!('de))
73 .into_iter()
74 .chain(generics.params)
75 .collect();
76 let (impl_generics, _, _) = generics.split_for_impl();
77 impl_generics.to_tokens(tokens);
78 }
79}
80
81struct CfgAttr {
88 condition: Meta,
89 _comma: Token![,],
90 metas: Punctuated<Meta, Token![,]>,
91}
92
93impl Parse for CfgAttr {
94 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
95 Ok(Self {
96 condition: input.parse()?,
97 _comma: input.parse()?,
98 metas: Punctuated::parse_terminated(input)?,
99 })
100 }
101}
102
103pub(crate) fn has_derive_jsonschema(input: TokenStream) -> syn::Result<SchemaFieldConfig> {
105 fn parse_derive_args(input: ParseStream<'_>) -> syn::Result<Punctuated<Path, Token![,]>> {
106 Punctuated::parse_terminated_with(input, Path::parse_mod_style)
107 }
108
109 fn eval_metas<'a>(metas: impl IntoIterator<Item = &'a Meta>) -> syn::Result<SchemaFieldConfig> {
110 metas
111 .into_iter()
112 .map(eval_meta)
113 .try_fold(
114 SchemaFieldConfig::False,
115 |state, result| Ok(state | result?),
116 )
117 }
118
119 fn eval_meta(meta: &Meta) -> syn::Result<SchemaFieldConfig> {
120 match meta.path() {
121 path if path.is_ident("cfg_attr") => {
122 let CfgAttr {
123 condition, metas, ..
124 } = meta.require_list()?.parse_args()?;
125
126 Ok(eval_metas(&metas)? & SchemaFieldConfig::Lazy(condition.into()))
127 }
128 path if path.is_ident("derive") => {
129 let config = meta
130 .require_list()?
131 .parse_args_with(parse_derive_args)?
132 .into_iter()
133 .any(|Path { segments, .. }| {
134 match segments.last() {
139 Some(PathSegment { ident, .. }) => ident == "JsonSchema",
140 _ => false,
141 }
142 })
143 .then_some(SchemaFieldConfig::True)
144 .unwrap_or_default();
145
146 Ok(config)
147 }
148 _ => Ok(SchemaFieldConfig::False),
149 }
150 }
151
152 let DeriveInput { attrs, .. } = syn::parse(input)?;
153 let metas = attrs.iter().map(|Attribute { meta, .. }| meta);
154 eval_metas(metas)
155}
156
157pub(crate) type SchemaFieldConfig = LazyBool<SchemaFieldCondition>;
159
160impl From<Meta> for SchemaFieldConfig {
161 fn from(meta: Meta) -> Self {
162 Self::Lazy(meta.into())
163 }
164}
165
166#[derive(Clone, Debug, Eq, Hash, PartialEq)]
167pub(crate) struct SchemaFieldCondition(pub(crate) Meta);
168
169impl BitAnd for SchemaFieldCondition {
170 type Output = Self;
171
172 fn bitand(self, Self(rhs): Self) -> Self::Output {
173 let Self(lhs) = self;
174 Self(parse_quote!(all(#lhs, #rhs)))
175 }
176}
177
178impl BitAnd<&SchemaFieldCondition> for SchemaFieldCondition {
179 type Output = Self;
180
181 fn bitand(self, Self(rhs): &Self) -> Self::Output {
182 let Self(lhs) = self;
183 Self(parse_quote!(all(#lhs, #rhs)))
184 }
185}
186
187impl BitOr for SchemaFieldCondition {
188 type Output = Self;
189
190 fn bitor(self, Self(rhs): Self) -> Self::Output {
191 let Self(lhs) = self;
192 Self(parse_quote!(any(#lhs, #rhs)))
193 }
194}
195
196impl Not for SchemaFieldCondition {
197 type Output = Self;
198
199 fn not(self) -> Self::Output {
200 let Self(condition) = self;
201 Self(parse_quote!(not(#condition)))
202 }
203}
204
205impl From<Meta> for SchemaFieldCondition {
206 fn from(meta: Meta) -> Self {
207 Self(meta)
208 }
209}
210
211pub(crate) fn schemars_with_attr_if(
214 attrs: &[Attribute],
215 filter: &[&str],
216) -> syn::Result<SchemaFieldConfig> {
217 fn eval_metas<'a>(
218 filter: &[&str],
219 metas: impl IntoIterator<Item = &'a Meta>,
220 ) -> syn::Result<SchemaFieldConfig> {
221 metas
222 .into_iter()
223 .map(|meta| eval_meta(filter, meta))
224 .try_fold(
225 SchemaFieldConfig::False,
226 |state, result| Ok(state | result?),
227 )
228 }
229
230 fn eval_meta(filter: &[&str], meta: &Meta) -> syn::Result<SchemaFieldConfig> {
231 match meta.path() {
232 path if path.is_ident("cfg_attr") => {
233 let CfgAttr {
234 condition, metas, ..
235 } = meta.require_list()?.parse_args()?;
236
237 Ok(eval_metas(filter, &metas)? & SchemaFieldConfig::from(condition))
238 }
239 path if path.is_ident("schemars") => {
240 let config = meta
241 .require_list()?
242 .parse_args_with(<Punctuated<Meta, Token![,]>>::parse_terminated)?
243 .into_iter()
244 .any(|meta| match meta.path().get_ident() {
245 Some(ident) => filter.iter().any(|relevant| ident == relevant),
246 _ => false,
247 })
248 .then_some(SchemaFieldConfig::True)
249 .unwrap_or_default();
250 Ok(config)
251 }
252 _ => Ok(SchemaFieldConfig::False),
253 }
254 }
255
256 let metas = attrs.iter().map(|Attribute { meta, .. }| meta);
257 eval_metas(filter, metas)
258}