1use alloc::{boxed::Box, vec::Vec};
4
5use proc_macro2::TokenStream;
6use syn::{Ident, Path};
7
8use crate::{
9 enumerate,
10 field::{FieldRef, Fields},
11 format::Format,
12 resolve::Resolve as _,
13 semantics::{
14 Conversion, Display, Enumeration as ValidEnumeration, ErrorSource, Header, Structure as ValidStructure, Target as ValidTarget,
15 Variant as ValidVariant,
16 },
17 source::{Source, SourceShape},
18 structure,
19 syntax::Transparent,
20 target::Kind,
21};
22
23#[derive(Clone, Debug)]
28struct Validated<ValueType>(ValueType);
29
30impl<ValueType> Validated<ValueType> {
31 const fn new(value: ValueType) -> Self {
33 Self(value)
34 }
35
36 #[inline]
38 #[must_use]
39 fn into_inner(self) -> ValueType {
40 let Self(value) = self;
41
42 value
43 }
44}
45
46trait Validate {
48 type Output;
50
51 fn validate(self) -> syn::Result<Self::Output>;
58}
59
60#[derive(Clone, Debug)]
65pub struct ValidatedTarget(ValidTarget);
66
67impl ValidatedTarget {
68 #[inline]
75 pub fn expand(self) -> syn::Result<TokenStream> {
76 let Self(target) = self;
77
78 crate::expand::target(target)
79 }
80}
81
82pub fn target(target: Kind) -> syn::Result<ValidatedTarget> {
84 let target = match target {
85 Kind::Struct(structure) => {
86 let structure = structure.validate()?.into_inner();
87
88 ValidTarget::Struct(Box::new(structure))
89 }
90 Kind::Enum(enumeration) => {
91 let enumeration = enumeration.validate()?.into_inner();
92
93 ValidTarget::Enum(enumeration)
94 }
95 };
96
97 Ok(ValidatedTarget(target))
98}
99
100impl Validate for structure::Structure {
101 type Output = Validated<ValidStructure>;
103
104 #[inline]
107 fn validate(self) -> syn::Result<Self::Output> {
108 let (config, name, generics, fields, declaration) = self.parts();
109 let (inline, import) = config.parts();
110 let (format, display, source, transparent, from) = declaration.parts();
111 let declaration = Declaration {
112 subject: name.clone(),
113 fields: &fields,
114 format,
115 display,
116 source,
117 transparent,
118 from,
119 };
120 let (display, source, conversion) = declaration.validate()?;
121 let header = Header::new(inline, import, name, generics);
122
123 let structure = ValidStructure::new(header, fields, display, source, conversion);
124
125 Ok(Validated::new(structure))
126 }
127}
128
129impl Validate for enumerate::Enumeration {
130 type Output = Validated<ValidEnumeration>;
133
134 #[inline]
136 fn validate(self) -> syn::Result<Self::Output> {
137 let (config, name, generics, variants) = self.parts();
138 let (inline, import) = config.parts();
139 let mut validated = Vec::with_capacity(variants.len());
140
141 for variant in variants {
142 validated.push(variant.validate()?.into_inner());
143 }
144
145 let header = Header::new(inline, import, name, generics);
146
147 let enumeration = ValidEnumeration::new(header, validated);
148
149 Ok(Validated::new(enumeration))
150 }
151}
152
153impl Validate for enumerate::Variant {
154 type Output = Validated<ValidVariant>;
156
157 #[inline]
160 fn validate(self) -> syn::Result<Self::Output> {
161 let (name, fields, declaration) = self.parts();
162 let (format, display, source, transparent, from) = declaration.parts();
163 let declaration = Declaration {
164 subject: name.clone(),
165 fields: &fields,
166 format,
167 display,
168 source,
169 transparent,
170 from,
171 };
172 let (display, source, conversion) = declaration.validate()?;
173
174 let variant = ValidVariant::new(name, fields, display, source, conversion);
175
176 Ok(Validated::new(variant))
177 }
178}
179
180struct Declaration<'fields> {
182 subject: Ident,
184
185 fields: &'fields Fields,
187
188 format: Option<Format>,
190
191 display: Option<Path>,
193
194 source: Option<FieldRef>,
196
197 transparent: Option<Transparent>,
199
200 from: bool,
202}
203
204impl Declaration<'_> {
205 fn validate(self) -> syn::Result<(Display, ErrorSource, Option<Conversion>)> {
208 let Self {
209 subject,
210 fields,
211 format,
212 display,
213 source,
214 transparent,
215 from,
216 } = self;
217
218 if format.is_some() && display.is_some() {
219 return Err(syn::Error::new_spanned(
220 subject,
221 "error cannot declare both a format string and `display(...)`",
222 ));
223 }
224
225 if transparent.is_some() && (format.is_some() || display.is_some()) {
226 return Err(syn::Error::new_spanned(
227 subject,
228 "transparent error cannot also declare display formatting",
229 ));
230 }
231
232 if transparent.is_some() && source.is_some() {
233 return Err(syn::Error::new_spanned(
234 subject,
235 "transparent error cannot also declare an ordinary source",
236 ));
237 }
238
239 if transparent.is_some() && from {
240 return Err(syn::Error::new_spanned(subject, "transparent error cannot also derive `From`"));
241 }
242
243 if let Some(Transparent(field_ref)) = transparent {
244 let field = field_ref.resolve(fields)?;
245 let sole = fields.sole()?;
246
247 if field != sole {
248 return Err(syn::Error::new_spanned(subject, "transparent error must target its sole field"));
249 }
250
251 let source = Source::new(fields, field);
252
253 if matches!(source.shape(), SourceShape::Optional | SourceShape::OptionalBoxed) {
254 return Err(syn::Error::new_spanned(subject, "transparent error source cannot be optional"));
255 }
256
257 let display = Display::Transparent(field);
258 let source = ErrorSource::Transparent(source);
259
260 return Ok((display, source, None));
261 }
262
263 let display = match (format, display) {
264 (Some(format), None) => Display::Format(format.resolve(fields)?),
265 (None, Some(path)) => Display::Custom(path),
266 (None, None) => {
267 return Err(syn::Error::new_spanned(
268 subject,
269 "error requires a format string, `display(...)`, or `transparent(...)`",
270 ));
271 }
272 (Some(_), Some(_)) => {
273 return Err(syn::Error::new_spanned(subject, "error has conflicting display declarations"));
274 }
275 };
276
277 let conversion = if from {
278 let field = fields.sole()?;
279 let field_type = fields.ty(field).clone();
280
281 Some(Conversion::new(field, field_type))
282 } else {
283 None
284 };
285
286 let source = match (source, conversion.as_ref()) {
287 (Some(field_ref), Some(conversion)) => {
288 let field = field_ref.resolve(fields)?;
289
290 if field != conversion.field() {
291 return Err(syn::Error::new_spanned(
292 subject,
293 "`from` conversion and explicit source must refer to the same field",
294 ));
295 }
296
297 ErrorSource::Field(Source::new(fields, field))
298 }
299 (Some(field_ref), None) => {
300 let field = field_ref.resolve(fields)?;
301
302 ErrorSource::Field(Source::new(fields, field))
303 }
304 (None, Some(conversion)) => ErrorSource::Field(Source::new(fields, conversion.field())),
305 (None, None) => ErrorSource::None,
306 };
307
308 Ok((display, source, conversion))
309 }
310}