schemars_derive/ast/
mod.rs1mod from_serde;
2
3use crate::attr::{ContainerAttrs, FieldAttrs, VariantAttrs};
4use crate::idents::{GENERATOR, SCHEMA};
5use from_serde::FromSerde;
6use proc_macro2::TokenStream;
7use serde_derive_internals::ast as serde_ast;
8use serde_derive_internals::{Ctxt, Derive};
9use std::collections::BTreeSet;
10
11pub struct Container<'a> {
12 pub ident: syn::Ident,
13 pub serde_attrs: serde_derive_internals::attr::Container,
14 pub data: Data<'a>,
15 pub generics: syn::Generics,
16 pub attrs: ContainerAttrs,
17 pub rename_type_params: BTreeSet<&'a syn::Ident>,
20 pub relevant_type_params: BTreeSet<&'a syn::Ident>,
23}
24
25pub enum Data<'a> {
26 Enum(Vec<Variant<'a>>),
27 Struct(serde_ast::Style, Vec<Field<'a>>),
28}
29
30pub struct Variant<'a> {
31 pub ident: syn::Ident,
32 pub serde_attrs: serde_derive_internals::attr::Variant,
33 pub style: serde_ast::Style,
34 pub fields: Vec<Field<'a>>,
35 pub original: &'a syn::Variant,
36 pub attrs: VariantAttrs,
37}
38
39pub struct Field<'a> {
40 pub member: syn::Member,
41 pub serde_attrs: serde_derive_internals::attr::Field,
42 pub ty: &'a syn::Type,
43 pub original: &'a syn::Field,
44 pub attrs: FieldAttrs,
45}
46
47impl<'a> Container<'a> {
48 pub fn from_ast(item: &'a syn::DeriveInput) -> syn::Result<Container<'a>> {
49 let ctxt = Ctxt::new();
50 let result = serde_ast::Container::from_ast(&ctxt, item, Derive::Deserialize)
51 .ok_or(())
52 .map(|serde| Self::from_serde(&ctxt, serde));
53
54 ctxt.check()
55 .map(|()| result.expect("from_ast set no errors on Ctxt, so should have returned Ok"))
56 }
57
58 pub fn transparent_field(&'a self) -> Option<&'a Field<'a>> {
59 if self.serde_attrs.transparent() {
60 if let Data::Struct(_, fields) = &self.data {
61 return fields.iter().find(|f| f.serde_attrs.transparent());
62 }
63 }
64
65 None
66 }
67
68 pub fn add_mutators(&self, mutators: &mut Vec<TokenStream>) {
69 self.attrs.common.add_mutators(mutators);
70 }
71
72 pub fn name(&'a self) -> std::borrow::Cow<'a, str> {
73 if self.attrs.rename_format_string.is_none() {
74 if let Some(remote_name) = self.serde_attrs.remote().and_then(|r| r.segments.last()) {
75 return remote_name.ident.to_string().into();
76 }
77 }
78
79 self.serde_attrs.name().deserialize_name().into()
80 }
81}
82
83impl Variant<'_> {
84 pub fn name(&self) -> Name {
85 Name(self.serde_attrs.name())
86 }
87
88 pub fn is_unit(&self) -> bool {
89 matches!(self.style, serde_ast::Style::Unit)
90 }
91
92 pub fn add_mutators(&self, mutators: &mut Vec<TokenStream>) {
93 self.attrs.common.add_mutators(mutators);
94 }
95
96 pub fn with_contract_check(&self, action: TokenStream) -> TokenStream {
97 with_contract_check(
98 self.serde_attrs.skip_deserializing(),
99 self.serde_attrs.skip_serializing(),
100 action,
101 )
102 }
103}
104
105impl Field<'_> {
106 pub fn name(&self) -> Name {
107 Name(self.serde_attrs.name())
108 }
109
110 pub fn add_mutators(&self, mutators: &mut Vec<TokenStream>) {
111 self.attrs.common.add_mutators(mutators);
112 self.attrs.validation.add_mutators(mutators);
113
114 if self.serde_attrs.skip_deserializing() {
115 mutators.push(quote! {
116 #SCHEMA.insert("readOnly".into(), true.into());
117 });
118 }
119 if self.serde_attrs.skip_serializing() {
120 mutators.push(quote! {
121 #SCHEMA.insert("writeOnly".into(), true.into());
122 });
123 }
124 }
125
126 pub fn with_contract_check(&self, action: TokenStream) -> TokenStream {
127 with_contract_check(
128 self.serde_attrs.skip_deserializing(),
129 self.serde_attrs.skip_serializing(),
130 action,
131 )
132 }
133}
134
135pub struct Name<'a>(&'a serde_derive_internals::attr::Name);
136
137impl quote::ToTokens for Name<'_> {
138 fn to_tokens(&self, tokens: &mut TokenStream) {
139 let ser_name = self.0.serialize_name();
140 let de_name = self.0.deserialize_name();
141 if ser_name == de_name {
142 ser_name.to_tokens(tokens);
143 } else {
144 quote! {
145 if #GENERATOR.contract().is_serialize() {
146 #ser_name
147 } else {
148 #de_name
149 }
150 }
151 .to_tokens(tokens);
152 }
153 }
154}
155
156fn with_contract_check(
157 skip_deserializing: bool,
158 skip_serializing: bool,
159 action: TokenStream,
160) -> TokenStream {
161 match (skip_deserializing, skip_serializing) {
162 (true, true) => TokenStream::new(),
163 (true, false) => quote! {
164 if #GENERATOR.contract().is_serialize() {
165 #action
166 }
167 },
168 (false, true) => quote! {
169 if #GENERATOR.contract().is_deserialize() {
170 #action
171 }
172 },
173 (false, false) => action,
174 }
175}