1use alloc::vec::Vec;
4
5use proc_macro2::Span;
6use syn::{Data, DataEnum, DataStruct, DeriveInput, Variant as SynVariant, spanned::Spanned};
7
8use crate::{
9 diagnostic::Errors,
10 enumerate::{Enumeration, Variant},
11 field::{FieldRef, Fields},
12 format::Format,
13 structure::Structure,
14 syntax::{Config, Declaration, Import, Inline, Param, ParamKind, Transparent},
15 validate::ValidatedTarget,
16};
17
18#[derive(Clone, Debug, PartialEq, Eq, Hash)]
20pub struct Target(Kind);
21
22#[derive(Clone, Debug, PartialEq, Eq, Hash)]
24pub enum Kind {
25 Enum(Enumeration),
27
28 Struct(Structure),
30}
31
32impl Target {
33 #[inline]
35 pub fn input(input: &DeriveInput) -> syn::Result<Self> {
36 match &input.data {
37 Data::Struct(data) => Self::structure(input, data),
38 Data::Enum(data) => Self::enumeration(input, data),
39 Data::Union(..) => Err(syn::Error::new_spanned(&input.ident, "unions cannot derive Error")),
40 }
41 }
42
43 #[inline]
50 pub fn validate(self) -> syn::Result<ValidatedTarget> {
51 let Self(kind) = self;
52
53 crate::validate::target(kind)
54 }
55
56 fn structure(input: &DeriveInput, data: &DataStruct) -> syn::Result<Self> {
58 let (params, ..) = Param::classify(&input.attrs)?;
59 let mut format = Bucket::<Format>::new(&input.ident);
60 let mut display = Bucket::<syn::Path>::new(&input.ident);
61 let mut source = Bucket::<FieldRef>::new(&input.ident);
62 let mut transparent = Bucket::<Transparent>::new(&input.ident);
63 let mut from = Bucket::<()>::new(&input.ident);
64 let mut inline = Bucket::<Inline>::new(&input.ident);
65 let mut import = Bucket::<Import>::new(&input.ident);
66
67 for param in params {
68 let (name, kind) = param.parts();
69 match kind {
70 ParamKind::Format(value) => format.push((name, value)),
71 ParamKind::Display(value) => display.push((name, value)),
72 ParamKind::Source(value) => source.push((name, value)),
73 ParamKind::Transparent(value) => transparent.push((name, value)),
74 ParamKind::From => from.push((name, ())),
75 ParamKind::Inline(value) => inline.push((name, value)),
76 ParamKind::Import(value) => import.push((name, value)),
77 }
78 }
79
80 let inline = inline.optional()?;
81 let import = import.optional()?;
82 let name = input.ident.clone();
83 let generics = input.generics.clone();
84 let fields = Fields::from_syn(&data.fields)?;
85 let format = format.optional()?;
86 let display = display.optional()?;
87 let source = source.optional()?;
88 let transparent = transparent.optional()?;
89 let from = from.optional()?.is_some();
90 let config = Config::new(inline, import);
91 let declaration = Declaration::new(format, display, source, transparent, from);
92 let structure = Structure::new(config, name, generics, fields, declaration);
93
94 Ok(Self(Kind::Struct(structure)))
95 }
96
97 fn enumeration(input: &DeriveInput, data: &DataEnum) -> syn::Result<Self> {
100 let (params, ..) = Param::classify(&input.attrs)?;
101 let mut inline = Bucket::<Inline>::new(&input.ident);
102 let mut import = Bucket::<Import>::new(&input.ident);
103 let mut errors = Errors::new();
104
105 for param in params {
106 let (name, kind) = param.parts();
107 match kind {
108 ParamKind::Inline(value) => inline.push((name, value)),
109 ParamKind::Import(value) => import.push((name, value)),
110 _ => errors.push(syn::Error::new_spanned(
111 name,
112 "only `inline` and `import` are valid on an error enum",
113 )),
114 }
115 }
116
117 let mut variants = Vec::with_capacity(data.variants.len());
118
119 for variant in &data.variants {
120 match Self::variant(variant) {
121 Ok(variant) => variants.push(variant),
122 Err(error) => errors.push(error),
123 }
124 }
125
126 let inline = inline.optional()?;
127 let import = import.optional()?;
128 let name = input.ident.clone();
129 let generics = input.generics.clone();
130 let config = Config::new(inline, import);
131 let enumeration = Enumeration::new(config, name, generics, variants);
132
133 errors.finish(Self(Kind::Enum(enumeration)))
134 }
135
136 fn variant(variant: &SynVariant) -> syn::Result<Variant> {
138 let (params, ..) = Param::classify(&variant.attrs)?;
139 let mut format = Bucket::<Format>::new(&variant.ident);
140 let mut display = Bucket::<syn::Path>::new(&variant.ident);
141 let mut source = Bucket::<FieldRef>::new(&variant.ident);
142 let mut transparent = Bucket::<Transparent>::new(&variant.ident);
143 let mut from = Bucket::<()>::new(&variant.ident);
144 let mut errors = Errors::new();
145
146 for param in params {
147 let (name, kind) = param.parts();
148 match kind {
149 ParamKind::Format(value) => format.push((name, value)),
150 ParamKind::Display(value) => display.push((name, value)),
151 ParamKind::Source(value) => source.push((name, value)),
152 ParamKind::Transparent(value) => transparent.push((name, value)),
153 ParamKind::From => from.push((name, ())),
154 ParamKind::Inline(..) | ParamKind::Import(..) => {
155 errors.push(syn::Error::new_spanned(name, "this parameter belongs on the enum"))
156 }
157 }
158 }
159
160 let name = variant.ident.clone();
161 let fields = Fields::from_syn(&variant.fields)?;
162 let format = format.optional()?;
163 let display = display.optional()?;
164 let source = source.optional()?;
165 let transparent = transparent.optional()?;
166 let from = from.optional()?.is_some();
167 let declaration = Declaration::new(format, display, source, transparent, from);
168 let variant = Variant::new(name, fields, declaration);
169
170 errors.finish(variant)
171 }
172}
173
174struct Bucket<ValueType> {
178 first: Option<(Span, ValueType)>,
180
181 extra: Vec<(Span, ValueType)>,
183}
184
185impl<ValueType> Bucket<ValueType> {
186 const fn new<SubjectType>(_subject: &SubjectType) -> Self
188 where
189 SubjectType: Spanned,
190 {
191 let first = None;
192 let extra = Vec::new();
193
194 Self { first, extra }
195 }
196
197 fn push<SpanType>(&mut self, (spanned, value): (SpanType, ValueType))
199 where
200 SpanType: Spanned,
201 {
202 let Self { first, extra } = self;
203 let span = spanned.span();
204
205 if first.is_some() {
206 extra.push((span, value));
207 } else {
208 *first = Some((span, value));
209 }
210 }
211
212 fn optional(self) -> syn::Result<Option<ValueType>> {
215 let Self { first, extra } = self;
216
217 match first {
218 Some((_span, value)) if extra.is_empty() => Ok(Some(value)),
219 Some((span, _)) => {
220 let mut error = syn::Error::new(span, "parameter declared more than once");
221
222 for (extra_span, _) in extra {
223 error.combine(syn::Error::new(extra_span, "duplicate parameter"));
224 }
225
226 Err(error)
227 }
228 None => Ok(None),
229 }
230 }
231}