1use proc_macro2::{Span, TokenStream as TokenStream2};
18use syn::punctuated::Punctuated;
19use syn::spanned::Spanned;
20use syn::{
21 Attribute, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Field, Fields,
22 FieldsNamed, FieldsUnnamed, Ident, Result, Type,
23};
24
25const NAME: &str = "from";
26const EXAMPLE: &str = r#"#[from(::std::fmt::Error)]"#;
27
28#[derive(Clone, PartialEq, Eq, Debug)]
29enum InstructionEntity {
30 Default,
31 DefaultEnumFields {
32 variant: Ident,
33 fields: Vec<Ident>,
34 },
35 Unit {
36 variant: Option<Ident>,
37 },
38 Named {
39 variant: Option<Ident>,
40 field: Ident,
41 other: Vec<Ident>,
42 },
43 Unnamed {
44 variant: Option<Ident>,
45 index: usize,
46 total: usize,
47 },
48}
49
50impl InstructionEntity {
51 pub fn with_fields(fields: &Fields, variant: Option<Ident>) -> Result<Self> {
52 let res = match (fields.len(), variant, fields.clone(), fields.iter().next().cloned()) {
53 (0, Some(v), ..) => InstructionEntity::Unit { variant: Some(v) },
54 (_, variant, Fields::Unit, ..) => InstructionEntity::Unit { variant },
55 (1, variant, Fields::Named(f), Some(Field { ident: Some(i), .. })) => {
56 InstructionEntity::Named {
57 variant,
58 field: i.clone(),
59 other: f
60 .named
61 .iter()
62 .filter_map(|f| f.ident.clone())
63 .filter(|ident| ident != &i)
64 .collect(),
65 }
66 }
67 (1, _, Fields::Named(_), ..) => {
68 unreachable!("If we have named field, it will match previous option")
69 }
70 (_, Some(variant), Fields::Named(f), ..) => InstructionEntity::DefaultEnumFields {
71 variant,
72 fields: f.named.iter().filter_map(|f| f.ident.clone()).collect(),
73 },
74 (len, variant, Fields::Unnamed(_), ..) => InstructionEntity::Unnamed {
75 variant,
76 index: 0,
77 total: len,
78 },
79 (_, None, ..) => InstructionEntity::Default,
80 };
81 Ok(res)
82 }
83
84 pub fn with_field(
85 index: usize,
86 total: usize,
87 field: &Field,
88 fields: &Fields,
89 variant: Option<Ident>,
90 ) -> Self {
91 if let Some(ref ident) = field.ident {
92 InstructionEntity::Named {
93 variant,
94 field: ident.clone(),
95 other: fields
96 .iter()
97 .filter_map(|f| f.ident.clone())
98 .filter(|i| ident != i)
99 .collect(),
100 }
101 } else {
102 InstructionEntity::Unnamed {
103 variant,
104 index,
105 total,
106 }
107 }
108 }
109
110 pub fn into_token_stream2(self) -> TokenStream2 {
111 match self {
112 InstructionEntity::Default => quote! {
113 Self::default()
114 },
115 InstructionEntity::Unit { variant } => {
116 let var = variant.map_or(quote! {}, |v| quote! {:: #v});
117 quote! { Self #var }
118 }
119 InstructionEntity::Named {
120 variant: None,
121 field,
122 ..
123 } => {
124 quote! {
125 Self { #field: v.into(), ..Default::default() }
126 }
127 }
128 InstructionEntity::Named {
129 variant: Some(var),
130 field,
131 other,
132 } => {
133 quote! {
134 Self :: #var { #field: v.into(), #( #other: Default::default(), )* }
135 }
136 }
137 InstructionEntity::Unnamed {
138 variant,
139 index,
140 total,
141 } => {
142 let var = variant.map_or(quote! {}, |v| quote! {:: #v});
143 let prefix = (0..index).fold(TokenStream2::new(), |mut stream, _| {
144 stream.extend(quote! {Default::default(),});
145 stream
146 });
147 let suffix = ((index + 1)..total).fold(TokenStream2::new(), |mut stream, _| {
148 stream.extend(quote! {Default::default(),});
149 stream
150 });
151 quote! {
152 Self #var ( #prefix v.into(), #suffix )
153 }
154 }
155 InstructionEntity::DefaultEnumFields { variant, fields } => {
156 quote! {
157 Self #variant { #( #fields: Default::default() )* }
158 }
159 }
160 }
161 }
162}
163
164#[derive(Clone)]
165struct InstructionEntry(pub Type, pub InstructionEntity);
166
167impl PartialEq for InstructionEntry {
168 fn eq(&self, other: &Self) -> bool {
170 let l = &self.0;
171 let r = &other.0;
172 let a = quote! { #l };
173 let b = quote! { #r };
174 format!("{}", a) == format!("{}", b)
175 }
176}
177
178impl InstructionEntry {
179 pub fn with_type(ty: &Type, entity: &InstructionEntity) -> Self {
180 Self(ty.clone(), entity.clone())
181 }
182
183 pub fn parse(
184 fields: &Fields,
185 attrs: &[Attribute],
186 entity: InstructionEntity,
187 ) -> Result<Vec<InstructionEntry>> {
188 let mut list = Vec::<InstructionEntry>::new();
189 for attr in attrs.iter().filter(|attr| attr.path.is_ident(NAME)) {
190 if attr.tokens.is_empty() {
192 match (fields.len(), fields.iter().next()) {
193 (1, Some(field)) => list.push(InstructionEntry::with_type(&field.ty, &entity)),
194 _ => {
195 return Err(attr_err!(
196 attr,
197 "empty attribute is allowed only for entities with a single field; \
198 for multi-field entities specify the attribute right ahead of the \
199 target field"
200 ));
201 }
202 }
203 } else {
204 list.push(InstructionEntry::with_type(&attr.parse_args()?, &entity));
205 }
206 }
207 Ok(list)
208 }
209}
210
211#[derive(Default)]
212struct InstructionTable(Vec<InstructionEntry>);
213
214impl InstructionTable {
215 pub fn new() -> Self { Default::default() }
216
217 pub fn parse(
218 &mut self,
219 fields: &Fields,
220 attrs: &[Attribute],
221 variant: Option<Ident>,
222 ) -> Result<&Self> {
223 let entity = InstructionEntity::with_fields(fields, variant.clone())?;
224 self.extend(InstructionEntry::parse(fields, attrs, entity.clone())?)?;
225 for (index, field) in fields.iter().enumerate() {
226 let mut punctuated = Punctuated::new();
227 punctuated.push_value(field.clone());
228 self.extend(InstructionEntry::parse(
229 &field.ident.as_ref().map_or(
230 Fields::Unnamed(FieldsUnnamed {
231 paren_token: Default::default(),
232 unnamed: punctuated.clone(),
233 }),
234 |_| {
235 Fields::Named(FieldsNamed {
236 brace_token: Default::default(),
237 named: punctuated,
238 })
239 },
240 ),
241 &field.attrs,
242 InstructionEntity::with_field(index, fields.len(), field, fields, variant.clone()),
243 )?)?;
244 }
245 if variant.is_none() && fields.len() == 1 && self.0.is_empty() {
246 let field = fields
247 .into_iter()
248 .next()
249 .expect("we know we have at least one item");
250 self.push(InstructionEntry::with_type(&field.ty, &entity));
251 }
252 Ok(self)
253 }
254
255 fn push(&mut self, item: InstructionEntry) { self.0.push(item) }
256
257 fn extend<T>(&mut self, list: T) -> Result<usize>
258 where T: IntoIterator<Item = InstructionEntry> {
259 let mut count = 0;
260 for entry in list {
261 self.0.iter().find(|e| *e == &entry).map_or(Ok(()), |_| {
262 Err(Error::new(
263 Span::call_site(),
264 format!("Attribute `#[{}]`: repeated use of type `{}`", NAME, quote! {ty}),
265 ))
266 })?;
267 self.0.push(entry);
268 count += 1;
269 }
270 Ok(count)
271 }
272
273 pub fn into_token_stream2(self, input: &DeriveInput) -> TokenStream2 {
274 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
275 let ident_name = &input.ident;
276
277 self.0.into_iter().fold(TokenStream2::new(), |mut stream, InstructionEntry(from, entity)| {
278 let convert = entity.into_token_stream2();
279 stream.extend(quote! {
280 #[automatically_derived]
281 impl #impl_generics ::core::convert::From<#from> for #ident_name #ty_generics #where_clause {
282 fn from(v: #from) -> Self {
283 #convert
284 }
285 }
286 });
287 stream
288 })
289 }
290}
291
292pub(crate) fn inner(input: DeriveInput) -> Result<TokenStream2> {
293 match input.data {
294 Data::Struct(ref data) => inner_struct(&input, data),
295 Data::Enum(ref data) => inner_enum(&input, data),
296 Data::Union(ref data) => inner_union(&input, data),
297 }
298}
299
300fn inner_struct(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream2> {
301 let mut instructions = InstructionTable::new();
302 instructions.parse(&data.fields, &input.attrs, None)?;
303 Ok(instructions.into_token_stream2(input))
304}
305
306fn inner_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream2> {
307 input
309 .attrs
310 .iter()
311 .find(|attr| attr.path.is_ident(NAME))
312 .map_or(Ok(()), |a| {
313 Err(attr_err!(
314 a,
315 "top-level attribute is not allowed, use it for specific fields or variants"
316 ))
317 })?;
318
319 let mut instructions = InstructionTable::new();
320 for v in &data.variants {
321 instructions.parse(&v.fields, &v.attrs, Some(v.ident.clone()))?;
322 }
323 Ok(instructions.into_token_stream2(input))
324}
325
326fn inner_union(input: &DeriveInput, data: &DataUnion) -> Result<TokenStream2> {
327 let mut instructions = InstructionTable::new();
328 instructions.parse(&Fields::Named(data.fields.clone()), &input.attrs, None)?;
329 Ok(instructions.into_token_stream2(input))
330}