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 = if 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 SchemaFieldConfig::True
144 } else {
145 LazyBool::default()
146 };
147 Ok(config)
148 }
149 _ => Ok(SchemaFieldConfig::False),
150 }
151 }
152
153 let DeriveInput { attrs, .. } = syn::parse(input)?;
154 let metas = attrs.iter().map(|Attribute { meta, .. }| meta);
155 eval_metas(metas)
156}
157
158pub(crate) type SchemaFieldConfig = LazyBool<SchemaFieldCondition>;
160
161impl From<Meta> for SchemaFieldConfig {
162 fn from(meta: Meta) -> Self {
163 Self::Lazy(meta.into())
164 }
165}
166
167#[derive(Clone, Debug, Eq, Hash, PartialEq)]
168pub(crate) struct SchemaFieldCondition(pub(crate) Meta);
169
170impl BitAnd for SchemaFieldCondition {
171 type Output = Self;
172
173 fn bitand(self, Self(rhs): Self) -> Self::Output {
174 let Self(lhs) = self;
175 Self(parse_quote!(all(#lhs, #rhs)))
176 }
177}
178
179impl BitAnd<&SchemaFieldCondition> for SchemaFieldCondition {
180 type Output = Self;
181
182 fn bitand(self, Self(rhs): &Self) -> Self::Output {
183 let Self(lhs) = self;
184 Self(parse_quote!(all(#lhs, #rhs)))
185 }
186}
187
188impl BitOr for SchemaFieldCondition {
189 type Output = Self;
190
191 fn bitor(self, Self(rhs): Self) -> Self::Output {
192 let Self(lhs) = self;
193 Self(parse_quote!(any(#lhs, #rhs)))
194 }
195}
196
197impl Not for SchemaFieldCondition {
198 type Output = Self;
199
200 fn not(self) -> Self::Output {
201 let Self(condition) = self;
202 Self(parse_quote!(not(#condition)))
203 }
204}
205
206impl From<Meta> for SchemaFieldCondition {
207 fn from(meta: Meta) -> Self {
208 Self(meta)
209 }
210}
211
212pub(crate) fn schemars_with_attr_if(
215 attrs: &[Attribute],
216 filter: &[&str],
217) -> syn::Result<SchemaFieldConfig> {
218 fn eval_metas<'a>(
219 filter: &[&str],
220 metas: impl IntoIterator<Item = &'a Meta>,
221 ) -> syn::Result<SchemaFieldConfig> {
222 metas
223 .into_iter()
224 .map(|meta| eval_meta(filter, meta))
225 .try_fold(
226 SchemaFieldConfig::False,
227 |state, result| Ok(state | result?),
228 )
229 }
230
231 fn eval_meta(filter: &[&str], meta: &Meta) -> syn::Result<SchemaFieldConfig> {
232 match meta.path() {
233 path if path.is_ident("cfg_attr") => {
234 let CfgAttr {
235 condition, metas, ..
236 } = meta.require_list()?.parse_args()?;
237
238 Ok(eval_metas(filter, &metas)? & SchemaFieldConfig::from(condition))
239 }
240 path if path.is_ident("schemars") => {
241 let config = if meta
242 .require_list()?
243 .parse_args_with(<Punctuated<Meta, Token![,]>>::parse_terminated)?
244 .into_iter()
245 .any(|meta| match meta.path().get_ident() {
246 Some(ident) => filter.iter().any(|relevant| ident == relevant),
247 _ => false,
248 }) {
249 SchemaFieldConfig::True
250 } else {
251 LazyBool::default()
252 };
253 Ok(config)
254 }
255 _ => Ok(SchemaFieldConfig::False),
256 }
257 }
258
259 let metas = attrs.iter().map(|Attribute { meta, .. }| meta);
260 eval_metas(filter, metas)
261}