optionable_codegen/
lib.rs

1//! The relevant main crate is [optionable](https://crates.io/crates/optionable). The docs can be found there.
2//!
3//! # Purpose
4//! This code generation `proc_macro2` library serves two purposes:
5//! - Used by [optionable_derive](https://crates.io/crates/optionable_derive) to implement the `#[derive(Optionable)]`-macro
6//!   re-exported by [optionable](https://crates.io/crates/optionable_derive).
7//! - Used by the [bin/codegen.rs](bin/codegen.rs) crate to support generating `Optionable`-implementations for external packages.
8//!   Due to the orphan rule  the generated code has to be added to the `Optionable`-package (PRs welcome).
9//!
10//! It has to be a separate crate from [optionable_derive](https://crates.io/crates/optionable_derive) as the proc-macro crates
11//! can't export its non-macro functions (even the `proc_macro2` ones) for the usage by the codegen part.
12use crate::FieldHandling::{IsOption, Other, Required};
13use darling::util::PathList;
14use darling::{FromAttributes, FromDeriveInput};
15use itertools::MultiUnzip;
16use proc_macro2::{Ident, Literal, Span, TokenStream};
17use quote::{format_ident, quote, ToTokens};
18use std::cmp::PartialEq;
19use std::default::Default;
20use std::{fmt, iter};
21use syn::punctuated::Punctuated;
22use syn::spanned::Spanned;
23use syn::token::Where;
24use syn::{
25    parse_quote, Attribute, Data, DeriveInput, Error, Field, Fields, GenericParam, LitStr, Meta, Path,
26    Token, Type, TypePath, WhereClause, WherePredicate,
27};
28
29const HELPER_IDENT: &str = "optionable";
30const HELPER_ATTR_IDENT: &str = "optionable_attr";
31const ERR_MSG_HELPER_ATTR_ENUM_VARIANTS: &str =
32    "#[optionable] helper attributes not supported on enum variant level.";
33
34#[derive(FromDeriveInput)]
35#[darling(attributes(optionable))]
36/// Helper attributes on the type definition level (attached to the `struct` or `enum` itself).
37struct TypeHelperAttributes {
38    /// Derive-macros that should be added to the optioned type
39    derive: Option<PathList>,
40    #[darling(default=default_suffix)]
41    /// Explicit suffix to use for the optioned type.
42    suffix: LitStr,
43    /// Skip generating `OptionableConvert` impl
44    no_convert: Option<()>,
45}
46
47#[derive(FromAttributes)]
48#[darling(attributes(optionable))]
49/// Helper attributes on the type definition level (attached to the `struct` or `enum` itself).
50struct FieldHelperAttributes {
51    /// Given field won't be optioned, it will also be required for the derived optioned type.
52    required: Option<()>,
53}
54
55fn default_suffix() -> LitStr {
56    LitStr::new("Opt", Span::call_site())
57}
58
59/// Returns the attribute for opting-out of `OptionableConvert`-impl generation.
60#[must_use]
61pub fn attribute_no_convert() -> Attribute {
62    parse_quote!(#[optionable(no_convert)])
63}
64
65/// Returns the attribute for setting a custom struct/enum name suffix instead of `Opt`.
66#[must_use]
67pub fn attribute_suffix(suffix: &str) -> Attribute {
68    parse_quote!(#[optionable(suffix=#suffix)])
69}
70
71/// Returns the attribute for setting that the given identifiers should be added as
72/// derive macro to the optioned struct/enum.
73#[must_use]
74pub fn attribute_derives(derives: &PathList) -> Attribute {
75    parse_quote!(#[optionable(derive(#(#derives),*))])
76}
77
78/// Derives the `Optionable`-trait from the main `optionable`-library.
79///
80/// # Errors
81/// - on misplaced helper attributes
82/// - internal implementation errors
83#[allow(clippy::too_many_lines)]
84pub fn derive_optionable(input: DeriveInput) -> syn::Result<TokenStream> {
85    let attrs = TypeHelperAttributes::from_derive_input(&input)?;
86    let forward_attrs = forwarded_attributes(&input.attrs);
87    let vis = input.vis;
88    let type_ident_opt = Ident::new(
89        &(input.ident.to_string() + &attrs.suffix.value()),
90        input.ident.span(),
91    );
92    let type_ident = &input.ident;
93
94    let generics_colon = (!input.generics.params.is_empty()).then(|| quote! {::});
95    let (impl_generics, ty_generics, _) = input.generics.split_for_impl();
96    let mut where_clause = input
97        .generics
98        .where_clause
99        .clone()
100        .unwrap_or_else(|| WhereClause {
101            where_token: Where::default(),
102            predicates: Punctuated::default(),
103        });
104    let where_clause_convert = attrs.no_convert.is_none().then(|| {
105        let mut where_clause = where_clause.clone();
106
107        patch_where_clause_bounds(&mut where_clause, &input.generics.params, |_| {
108            quote!(::optionable::OptionableConvert)
109        });
110        where_clause
111    });
112    patch_where_clause_bounds(&mut where_clause, &input.generics.params, |_| {
113        quote!(::optionable::Optionable)
114    });
115
116    // the impl statements are actually independent of deriving
117    // the relevant associated type #type_ident_opt referenced by them
118    let impls_optionable = quote! {
119        #[automatically_derived]
120        impl #impl_generics ::optionable::Optionable for #type_ident #ty_generics #where_clause {
121            type Optioned = #type_ident_opt #ty_generics;
122        }
123
124        #[automatically_derived]
125        impl #impl_generics ::optionable::Optionable for #type_ident_opt #ty_generics #where_clause {
126            type Optioned = #type_ident_opt #ty_generics;
127        }
128    };
129
130    // now we have to derive the actual implementation of #type_ident_opt
131    // and add the #impl from above
132    let derives = attrs.derive.unwrap_or_default();
133    let skip_optionable_if_serde_serialize = derives
134        .iter()
135        .any(is_serialize)
136        .then(|| quote!(#[serde(skip_serializing_if = "Option::is_none")]));
137    let derives = (!derives
138        .iter()
139        .map(ToTokens::to_token_stream)
140        .collect::<Vec<_>>()
141        .is_empty())
142    .then(|| quote! {#[derive(#(#derives),*)]});
143
144    match input.data {
145        Data::Struct(s) => {
146            let fields = into_field_handling(s.fields)?;
147            let unnamed_struct_semicolon =
148                (fields.struct_type == StructType::Unnamed).then(|| quote!(;));
149            let optioned_fields =
150                optioned_fields(&fields, skip_optionable_if_serde_serialize.as_ref());
151
152            let impl_optionable_convert = attrs.no_convert.is_none().then(|| {
153                let into_optioned_fields = into_optioned(&fields, |selector| quote! { self.#selector });
154                let try_from_optioned_fields =
155                    try_from_optioned(&fields, |selector| quote! { value.#selector });
156                let merge_fields = merge_fields(
157                    &fields,
158                    |selector| quote! { self.#selector },
159                    |selector| quote! { other.#selector },
160                    true);
161                quote! {
162                    #[automatically_derived]
163                    impl #impl_generics ::optionable::OptionableConvert for #type_ident #ty_generics #where_clause_convert {
164                        fn into_optioned(self) -> #type_ident_opt #ty_generics {
165                            #type_ident_opt #generics_colon #ty_generics #into_optioned_fields
166                        }
167
168                        fn try_from_optioned(value: #type_ident_opt #ty_generics) -> Result<Self,::optionable::optionable::Error>{
169                            Ok(Self #try_from_optioned_fields)
170                        }
171
172                        fn merge(&mut self, other: #type_ident_opt #ty_generics) -> Result<(), ::optionable::optionable::Error>{
173                            #merge_fields
174                            Ok(())
175                        }
176                    }
177                }
178            });
179
180            Ok(quote! {
181                #[automatically_derived]
182                #derives
183                #forward_attrs
184                #vis struct #type_ident_opt #impl_generics #where_clause #optioned_fields #unnamed_struct_semicolon
185
186                #impls_optionable
187
188                #impl_optionable_convert
189            })
190        }
191        Data::Enum(e) => {
192            let self_prefix = quote! {self_};
193            let other_prefix = quote! {other_};
194
195            let variants = e
196                .variants
197                .into_iter()
198                .map(|v| {
199                    error_on_helper_attributes(&v.attrs, ERR_MSG_HELPER_ATTR_ENUM_VARIANTS)?;
200                    Ok::<_, Error>((v.ident, into_field_handling(v.fields)?))
201                })
202                .collect::<Result<Vec<_>, _>>()?;
203
204            let optioned_variants = variants.iter().map(|(variant, f)| {
205                let fields = optioned_fields(f, skip_optionable_if_serde_serialize.as_ref());
206                quote!( #variant #fields )
207            });
208
209            let impl_optionable_convert = attrs.no_convert.is_none().then(|| {
210                let (into_variants, try_from_variants, merge_variants): (Vec<_>, Vec<_>, Vec<_>) = variants
211                    .iter()
212                    .map(|(variant, f)| {
213                        let fields_into = into_optioned(f, |selector| {
214                            format_ident!("{self_prefix}{selector}").to_token_stream()
215                        });
216                        let fields_try_from = try_from_optioned(f, |selector| {
217                            format_ident!("{other_prefix}{selector}").to_token_stream()
218                        });
219                        let fields_merge = merge_fields(f,
220                                                        |selector| format_ident!("{self_prefix}{selector}").to_token_stream(),
221                                                        |selector| format_ident!("{other_prefix}{selector}").to_token_stream(),
222                                                        false);
223                        let self_destructure = destructure(f, &self_prefix)?;
224                        let other_destructure = destructure(f, &other_prefix)?;
225                        Ok::<_, Error>((
226                            quote!( Self::#variant #self_destructure => #type_ident_opt::#variant #fields_into ),
227                            quote!( #type_ident_opt::#variant #other_destructure => Self::#variant #fields_try_from ),
228                            quote!( #type_ident_opt::#variant #other_destructure => {
229                                if let Self::#variant #self_destructure = self {
230                                    #fields_merge
231                                } else {
232                                    *self = Self::try_from_optioned(#type_ident_opt::#variant #other_destructure)?;
233                                }
234                            })
235                        ))
236                    })
237                    .collect::<Result<Vec<_>, _>>()?
238                    .into_iter().multiunzip();
239                Ok::<_, Error>(quote! {
240                    #[automatically_derived]
241                    impl #impl_generics ::optionable::OptionableConvert for #type_ident #ty_generics #where_clause_convert {
242                        fn into_optioned(self) -> #type_ident_opt #ty_generics {
243                            match self {
244                                #(#into_variants),*
245                            }
246                        }
247
248                        fn try_from_optioned(other: #type_ident_opt #ty_generics)->Result<Self,::optionable::optionable::Error>{
249                            Ok(match other{
250                                #(#try_from_variants),*
251                            })
252                        }
253
254                        fn merge(&mut self, other: #type_ident_opt #ty_generics) -> Result<(), ::optionable::optionable::Error>{
255                            match other{
256                                #(#merge_variants),*
257                            }
258                            Ok(())
259                        }
260                    }
261                })
262            }).transpose()?;
263
264            Ok(quote!(
265                #[automatically_derived]
266                #derives
267                #forward_attrs
268                #vis enum #type_ident_opt #impl_generics #where_clause {
269                    #(#optioned_variants),*
270                }
271
272                #impls_optionable
273
274                #impl_optionable_convert
275            ))
276        }
277        Data::Union(_) => error("#[derive(Optionable)] not supported for unit structs"),
278    }
279}
280
281/// Constructs a destructure selector for the given fields, e.g. a `std::vec!(field_a, field_b)`
282/// for a named enum or `std::vec!(0,1)` for an unnamed enum.
283fn destructure(fields: &FieldsParsed, prefix: &TokenStream) -> Result<TokenStream, Error> {
284    Ok(match fields.struct_type {
285        StructType::Named => {
286            let fields = fields
287                .fields
288                .iter()
289                .map(|f| {
290                    let ident = f.field.ident.as_ref().ok_or::<Error>(
291                        error::<_, Error>(format!(
292                            "expected field name but none present for {f:?}"
293                        ))
294                        .unwrap_err(),
295                    )?;
296                    let prefixed_ident = format_ident!("{1}{0}", ident.clone(), prefix.to_string());
297                    Ok::<_, Error>(quote! {#ident: #prefixed_ident})
298                })
299                .collect::<Result<Vec<_>, _>>()?;
300            quote! {{#(#fields),*}}
301        }
302        StructType::Unnamed => {
303            let indices: Vec<_> = (0..fields.fields.len())
304                .map(|i| {
305                    let prefixed = format_ident!("{1}{0}", i, prefix.to_string());
306                    quote! {#prefixed}
307                })
308                .collect();
309            quote! {(#(#indices),*)}
310        }
311        StructType::Unit => quote! {},
312    })
313}
314
315/// Returns a tokenstream for the optioned fields and potential convert implementation of the optioned object (struct/enum variants).
316/// The returned tokenstream will be of the form `{...}` for named fields and `(...)` for unnamed fields.
317/// Does not include any leading `struct/enum` keywords or any trailing `;`.
318fn optioned_fields(fields: &FieldsParsed, serde_attributes: Option<&TokenStream>) -> TokenStream {
319    let fields_token = fields.fields.iter().map(
320        |FieldParsed {
321             field: Field { attrs,vis, ident, ty, .. },
322             handling,
323         }| {
324            let forward_attrs = forwarded_attributes(attrs);
325            let optioned_ty = optioned_ty(ty);
326            let colon = ident.as_ref().map(|_| quote! {:});
327            match handling {
328                Required => quote! {#forward_attrs #vis #ident #colon #ty},
329                IsOption => quote! {#forward_attrs #serde_attributes #vis #ident #colon #optioned_ty},
330                Other => quote! {#forward_attrs #serde_attributes #vis #ident #colon Option<#optioned_ty>},
331            }
332        },
333    );
334    struct_wrapper(fields_token, &fields.struct_type)
335}
336
337/// Returns the field mapping implementation for `into_optioned`.
338/// The returned tokenstream will be of the form `{...}` for named fields and `(...)` for unnamed fields.
339/// Does not include any leading `struct/enum` keywords or any trailing `;`.
340fn into_optioned(
341    fields: &FieldsParsed,
342    self_selector_fn: impl Fn(&TokenStream) -> TokenStream,
343) -> TokenStream {
344    let fields_token = fields.fields.iter().enumerate().map(|(i, FieldParsed { field: Field { ident, ty, .. }, handling })| {
345        let colon = ident.as_ref().map(|_| quote! {:});
346        let selector = ident.as_ref().map_or_else(|| {
347            let i = Literal::usize_unsuffixed(i);
348            quote! {#i}
349        }, ToTokens::to_token_stream);
350        let self_selector = self_selector_fn(&selector);
351        match (handling, is_self_resolving_optioned(ty)) {
352            (Required, _) | (IsOption, true) => quote! {#ident #colon #self_selector},
353            (IsOption, false) => quote! {#ident #colon <#ty as ::optionable::OptionableConvert>::into_optioned(#self_selector)},
354            (Other, true) => quote! {#ident #colon Some(#self_selector)},
355            (Other, false) => quote! {#ident #colon Some(<#ty as ::optionable::OptionableConvert>::into_optioned(#self_selector))}
356        }
357    });
358    struct_wrapper(fields_token, &fields.struct_type)
359}
360
361/// Returns the field-mappings implementation for `try_from_optioned`.
362/// The returned tokenstream will be of the form `{...}` for named fields and `(...)` for unnamed fields.
363/// Does not include any leading `struct/enum` keywords or any trailing `;`.
364fn try_from_optioned(
365    fields: &FieldsParsed,
366    value_selector_fn: impl Fn(&TokenStream) -> TokenStream,
367) -> TokenStream {
368    let fields_token = fields.fields.iter().enumerate().map(|(i, FieldParsed { field: Field { ident, ty, .. }, handling })| {
369        let colon = ident.as_ref().map(|_| quote! {:});
370        let selector = ident.as_ref().map_or_else(|| {
371            let i = Literal::usize_unsuffixed(i);
372            quote! {#i}
373        }, ToTokens::to_token_stream);
374        let value_selector = value_selector_fn(&selector);
375        match (handling, is_self_resolving_optioned(ty)) {
376            (Required, _) | (IsOption, true) => quote! {#ident #colon value.#selector},
377            (IsOption, false) => quote! {
378                #ident #colon <#ty as ::optionable::OptionableConvert>::try_from_optioned(
379                    #value_selector
380                )?
381            },
382            (Other, true) => {
383                let selector_quoted = LitStr::new(&selector.to_string(), ident.span());
384                quote! {
385                    #ident #colon #value_selector.ok_or(optionable::optionable::Error{ missing_fields: std::vec![#selector_quoted] })?
386                }
387            }
388            (Other, false) => {
389                let selector_quoted = LitStr::new(&selector.to_string(), ident.span());
390                quote! {
391                    #ident #colon <#ty as ::optionable::OptionableConvert>::try_from_optioned(
392                        #value_selector.ok_or(optionable::optionable::Error{ missing_fields: std::vec![#selector_quoted] })?
393                    )?
394                }
395            }
396        }
397    });
398
399    struct_wrapper(fields_token, &fields.struct_type)
400}
401
402/// Returns the field-mappings implementation for `try_from_optioned`.
403/// The returned tokenstream will be of the form `{...}` for named fields and `(...)` for unnamed fields.
404/// Does not include any leading `struct/enum` keywords or any trailing `;`.
405///
406/// `merge_self_mut` should be true when the `self_selector` merge argument should be modified with a `& mut` on recursive calls.
407fn merge_fields(
408    fields: &FieldsParsed,
409    self_selector_fn: impl Fn(&TokenStream) -> TokenStream,
410    other_selector_fn: impl Fn(&TokenStream) -> TokenStream,
411    merge_self_mut: bool,
412) -> TokenStream {
413    let fields_token = fields.fields.iter().enumerate().map(
414        |(
415             i,
416             FieldParsed {
417                 field: Field { ident, ty, .. },
418                 handling,
419             },
420         )| {
421            let selector = ident.as_ref().map_or_else(
422                || {
423                    let i = Literal::usize_unsuffixed(i);
424                    quote! {#i}
425                },
426                ToTokens::to_token_stream,
427            );
428            let self_merge_mut_modifier = merge_self_mut.then(|| quote! {&mut});
429            let deref_modifier = (!merge_self_mut).then(|| quote! {*});
430            let self_selector = self_selector_fn(&selector);
431            let other_selector = other_selector_fn(&selector);
432            match (handling, is_self_resolving_optioned(ty)) {
433                (Required, _) | (IsOption, true) => quote! {#deref_modifier #self_selector = #other_selector;},
434                (IsOption, false) => quote! {
435                    <#ty as ::optionable::OptionableConvert>::merge(#self_merge_mut_modifier #self_selector, #other_selector)?;
436                },
437                (Other, true) => quote! {
438                    if let Some(other_value)=#other_selector{
439                        #deref_modifier #self_selector = other_value;
440                    }
441                },
442                (Other, false) => quote! {
443                    if let Some(other_value)=#other_selector{
444                        <#ty as ::optionable::OptionableConvert>::merge(#self_merge_mut_modifier #self_selector, other_value)?;
445                    }
446                }
447            }
448        },
449    );
450
451    quote! {
452        #(#fields_token)*
453    }
454}
455
456/// Resolves to a comma-serapated list of entries with a (...) wrapper for unnamed structs
457/// and {...} for named structs
458fn struct_wrapper(
459    tokens: impl Iterator<Item = TokenStream>,
460    struct_name_type: &StructType,
461) -> TokenStream {
462    match struct_name_type {
463        StructType::Named => quote!({#(#tokens),*}),
464        StructType::Unnamed => quote!((#(#tokens),*)),
465        StructType::Unit => quote!(),
466    }
467}
468
469/// Adjusts the where clause to add the provided predicate  type bounds.
470/// Basically the original where clause with a type bound to the preodicate added
471/// for every generic type parameter (todo: that is not excluded via the `required` helper attribute.)
472fn patch_where_clause_bounds(
473    where_clause: &mut WhereClause,
474    params: &Punctuated<GenericParam, Token![,]>,
475    predicate: impl Fn(&Type) -> TokenStream,
476) {
477    params.iter().for_each(|param| {
478        if let GenericParam::Type(type_param) = param {
479            let path = &Type::Path(TypePath {
480                qself: None,
481                path: type_param.ident.clone().into(),
482            });
483            add_where_clause_predicate(where_clause, path, &predicate);
484        }
485    });
486}
487
488/// Goes through the list of predicates and appends the new restriction to an already existing
489/// entry if found or creates a new one
490fn add_where_clause_predicate(
491    where_clause: &mut WhereClause,
492    ty: &Type,
493    entry: impl Fn(&Type) -> TokenStream,
494) {
495    let bounds = where_clause.predicates.iter_mut().find_map(|pred| {
496        if let WherePredicate::Type(pred_ty) = pred
497            && *ty == pred_ty.bounded_ty
498        {
499            Some(&mut pred_ty.bounds)
500        } else {
501            None
502        }
503    });
504    let entry = entry(ty);
505    if let Some(bounds) = bounds {
506        bounds.push(parse_quote!(#entry));
507    } else {
508        where_clause.predicates.push(parse_quote!(#ty: #entry));
509    }
510}
511
512/// Goes through the attributes, filters for our [`HELPER_IDENT`] helper-attribute identifier
513/// and reports an error if anything is found.
514fn error_on_helper_attributes(attrs: &[Attribute], err_msg: &'static str) -> syn::Result<()> {
515    if attrs
516        .iter()
517        .filter(|attr| {
518            println!("{}", attr.path().to_token_stream());
519            attr.path().is_ident(HELPER_IDENT)
520        })
521        .collect::<Vec<_>>()
522        .is_empty()
523    {
524        Ok(())
525    } else {
526        error(err_msg)
527    }
528}
529
530/// Extracts information about the fields we care about, like
531/// whether #[optional(required)] is set or whether the type is `Option<...>`.
532fn into_field_handling(fields: Fields) -> Result<FieldsParsed, Error> {
533    let struct_named = match &fields {
534        Fields::Named(_) => StructType::Named,
535        Fields::Unnamed(_) => StructType::Unnamed,
536        Fields::Unit => StructType::Unit,
537    };
538
539    let fields_iter: Box<dyn Iterator<Item = Field>> = match fields {
540        Fields::Named(f) => Box::new(f.named.into_iter()),
541        Fields::Unnamed(f) => Box::new(f.unnamed.into_iter()),
542        Fields::Unit => Box::new(iter::empty()),
543    };
544    let fields_with_handling = fields_iter
545        .map(|field| {
546            let attrs = FieldHelperAttributes::from_attributes(&field.attrs)?;
547            let handling = if attrs.required.is_some() {
548                Required
549            } else if is_option(&field.ty) {
550                IsOption
551            } else {
552                Other
553            };
554            Ok::<_, Error>(FieldParsed { field, handling })
555        })
556        .collect::<Result<Vec<_>, _>>()?;
557
558    Ok(FieldsParsed {
559        struct_type: struct_named,
560        fields: fields_with_handling,
561    })
562}
563
564/// How we handle different cases in order of importance/detection.
565/// E.g. if a field is `Required` we don't care whether it's of `Option` type or not.
566#[derive(Debug)]
567enum FieldHandling {
568    Required,
569    IsOption,
570    Other,
571}
572
573/// Just a combination of a field and which handling case it is
574#[derive(Debug)]
575struct FieldParsed {
576    field: Field,
577    handling: FieldHandling,
578}
579
580/// Type of the Struct we handle
581#[derive(Debug, PartialEq)]
582enum StructType {
583    Named,
584    Unnamed,
585    Unit,
586}
587
588/// Fields with extracted data how we want to handle them
589#[derive(Debug)]
590struct FieldsParsed {
591    struct_type: StructType,
592    fields: Vec<FieldParsed>,
593}
594
595/// Checks whether this type identifier is a `std::option::Option` or a shortened variant of it.
596fn is_option(ty: &Type) -> bool {
597    if let Type::Path(TypePath {
598        qself: _qself,
599        path,
600    }) = &ty
601        && {
602            let segments = &path.segments;
603            (segments.len() == 1 && segments[0].ident == "Option")
604                || (segments.len() == 2
605                    && segments[0].ident == "option"
606                    && segments[1].ident == "Option")
607                || (segments.len() == 3
608                    && segments[0].ident == "std"
609                    && segments[1].ident == "option"
610                    && segments[2].ident == "Option")
611        }
612    {
613        true
614    } else {
615        false
616    }
617}
618
619/// Checks whether this path is `serde::Serialize` or a shortened version of it.
620fn is_serialize(path: &Path) -> bool {
621    path.is_ident("Serialize") || {
622        let segments = &path.segments;
623        segments.len() == 2 && segments[0].ident == "serde" && segments[1].ident == "Serialize"
624    }
625}
626
627/// Tries to resolve the optioned type analogous to what we do in the main crate.
628/// Due to limitations to macro resolving (no order guaranteed) we have to have an explicit
629/// list of well-known types and their optioned types.
630/// For now limited to self-resolving (mostly primitive) types
631fn optioned_ty(ty: &Type) -> TokenStream {
632    if is_self_resolving_optioned(ty) {
633        ty.to_token_stream()
634    } else {
635        quote! { <#ty as ::optionable::Optionable>::Optioned }
636    }
637}
638
639const SELF_RESOLVING_TYPES: [&str; 17] = [
640    // Rust primitives don't have inner structure, https://doc.rust-lang.org/rust-by-example/primitives.html
641    "i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64", "u128", "usize", "f32",
642    "f64", "char", "bool", // Other types without inner structure
643    "String",
644];
645
646/// Checks when it is well known that the type resolves to itself as its `Optioned`.
647/// Limited to self-resolving (mostly primitive) types.
648fn is_self_resolving_optioned(ty: &Type) -> bool {
649    if let Type::Path(TypePath { qself, path }) = &ty
650        && qself.is_none()
651        && SELF_RESOLVING_TYPES.contains(&&*path.to_token_stream().to_string())
652    {
653        true
654    } else {
655        false
656    }
657}
658
659/// Extracts the `HELPER_ATTR_IDENT` attributes, unwraps them and returns them
660/// as `TokenStream` that can be used as respective attribute.
661fn forwarded_attributes(attrs: &[Attribute]) -> Option<TokenStream> {
662    let forward_attrs = attrs
663        .iter()
664        .filter_map(|attr| {
665            if !attr.path().is_ident(HELPER_ATTR_IDENT) {
666                return None;
667            }
668            match &attr.meta {
669                Meta::List(l) => Some(l.tokens.clone()),
670                _ => None,
671            }
672        })
673        .collect::<Vec<_>>();
674    (!forward_attrs.is_empty()).then(|| quote!(#[#(#forward_attrs),*]))
675}
676
677/// error just prepares an error message that references the source span
678pub(crate) fn error<S: AsRef<str> + fmt::Display, T>(msg: S) -> syn::Result<T> {
679    Err(Error::new(Span::call_site(), msg))
680}
681
682#[cfg(test)]
683mod tests {
684    use crate::derive_optionable;
685    use proc_macro2::TokenStream;
686    use quote::quote;
687
688    struct TestCase {
689        input: TokenStream,
690        output: TokenStream,
691    }
692
693    #[test]
694    #[allow(clippy::too_many_lines)]
695    fn test_optionable() {
696        let tcs = vec![
697            // named struct fields
698            TestCase {
699                input: quote! {
700                    #[derive(Optionable)]
701                    struct DeriveExample {
702                        name: String,
703                        pub surname: String,
704                    }
705                },
706                output: quote! {
707                    #[automatically_derived]
708                    struct DeriveExampleOpt {
709                        name: Option<String>,
710                        pub surname: Option<String>
711                    }
712
713                    #[automatically_derived]
714                    impl ::optionable::Optionable for DeriveExample {
715                        type Optioned = DeriveExampleOpt;
716                    }
717
718                    #[automatically_derived]
719                    impl ::optionable::Optionable for DeriveExampleOpt {
720                        type Optioned = DeriveExampleOpt;
721                    }
722
723                    #[automatically_derived]
724                    impl ::optionable::OptionableConvert for DeriveExample {
725                        fn into_optioned (self) -> DeriveExampleOpt {
726                            DeriveExampleOpt  {
727                                name: Some(self.name),
728                                surname:Some(self.surname)
729                            }
730                        }
731
732                        fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::optionable::Error> {
733                            Ok(Self{
734                                name: value.name.ok_or(optionable::optionable::Error { missing_fields: std::vec!["name"] })?,
735                                surname: value.surname.ok_or(optionable::optionable::Error { missing_fields: std::vec!["surname"] })?
736                            })
737                        }
738
739                        fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::optionable::Error> {
740                            if let Some(other_value) = other.name {
741                                self.name = other_value;
742                            }
743                            if let Some(other_value) = other.surname {
744                                self.surname = other_value;
745                            }
746                            Ok(())
747                        }
748                    }
749                },
750            },
751            // named struct fields, no convert impl
752            TestCase {
753                input: quote! {
754                    #[derive(Optionable)]
755                    #[optionable(no_convert)]
756                    struct DeriveExample {
757                        name: String,
758                        pub surname: String,
759                    }
760                },
761                output: quote! {
762                    #[automatically_derived]
763                    struct DeriveExampleOpt {
764                        name: Option<String>,
765                        pub surname: Option<String>
766                    }
767
768                    #[automatically_derived]
769                    impl ::optionable::Optionable for DeriveExample {
770                        type Optioned = DeriveExampleOpt;
771                    }
772
773                    #[automatically_derived]
774                    impl ::optionable::Optionable for DeriveExampleOpt {
775                        type Optioned = DeriveExampleOpt;
776                    }
777                },
778            },
779            // named struct fields with required fields
780            TestCase {
781                input: quote! {
782                    #[derive(Optionable)]
783                    struct DeriveExample {
784                        name: String,
785                        #[optionable(required)]
786                        pub surname: String,
787                    }
788                },
789                output: quote! {
790                    #[automatically_derived]
791                    struct DeriveExampleOpt {
792                        name: Option<String>,
793                        pub surname: String
794                    }
795
796                    #[automatically_derived]
797                    impl ::optionable::Optionable for DeriveExample {
798                        type Optioned = DeriveExampleOpt;
799                    }
800
801                    #[automatically_derived]
802                    impl ::optionable::Optionable for DeriveExampleOpt {
803                        type Optioned = DeriveExampleOpt;
804                    }
805
806                    #[automatically_derived]
807                    impl ::optionable::OptionableConvert for DeriveExample {
808                        fn into_optioned (self) -> DeriveExampleOpt {
809                            DeriveExampleOpt  {
810                                name: Some(self.name),
811                                surname: self.surname
812                            }
813                        }
814
815                        fn try_from_optioned(value:DeriveExampleOpt ) -> Result <Self, ::optionable::optionable::Error> {
816                            Ok (Self {
817                                name: value.name.ok_or(optionable::optionable::Error { missing_fields: std::vec! ["name"] })?,
818                                surname: value.surname
819                            })
820                        }
821
822                        fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::optionable::Error> {
823                            if let Some(other_value) = other.name {
824                                self.name = other_value;
825                            }
826                            self.surname = other.surname;
827                            Ok (())
828                        }
829                    }
830                },
831            },
832            // named struct fields with forwarded derives and Serialize annotations
833            TestCase {
834                input: quote! {
835                    #[derive(Optionable)]
836                    #[optionable(derive(Deserialize,Serialize),suffix="Ac")]
837                    #[optionable_attr(serde(rename_all = "camelCase"))]
838                    struct DeriveExample {
839                        #[optionable_attr(serde(rename = "firstName"))]
840                        name: String,
841                        middle_name: Option<String>,
842                        surname: String,
843                    }
844                },
845                output: quote! {
846                    #[automatically_derived]
847                    #[derive(Deserialize, Serialize)]
848                    #[serde(rename_all = "camelCase")]
849                    struct DeriveExampleAc {
850                        #[serde(rename = "firstName")]
851                        #[serde(skip_serializing_if = "Option::is_none")]
852                        name: Option<String>,
853                        #[serde(skip_serializing_if = "Option::is_none")]
854                        middle_name: <Option<String> as ::optionable::Optionable>::Optioned,
855                        #[serde(skip_serializing_if = "Option::is_none")]
856                        surname: Option<String>
857                    }
858
859                    #[automatically_derived]
860                    impl ::optionable::Optionable for DeriveExample {
861                        type Optioned = DeriveExampleAc;
862                    }
863
864                    #[automatically_derived]
865                    impl ::optionable::Optionable for DeriveExampleAc {
866                        type Optioned = DeriveExampleAc;
867                    }
868
869                    #[automatically_derived]
870                    impl ::optionable::OptionableConvert for DeriveExample {
871                        fn into_optioned (self) -> DeriveExampleAc {
872                            DeriveExampleAc  {
873                                name: Some(self.name),
874                                middle_name: <Option<String> as ::optionable::OptionableConvert>::into_optioned(self.middle_name),
875                                surname: Some(self.surname)
876                            }
877                        }
878
879                        fn try_from_optioned(value: DeriveExampleAc ) -> Result <Self, ::optionable::optionable::Error> {
880                            Ok(Self{
881                                name: value.name.ok_or(optionable::optionable::Error { missing_fields: std::vec!["name"]})?,
882                                middle_name: <Option<String> as ::optionable::OptionableConvert>::try_from_optioned(value.middle_name)?,
883                                surname: value.surname.ok_or(optionable::optionable::Error { missing_fields: std::vec!["surname"]})?
884                            })
885                        }
886
887                        fn merge(&mut self, other: DeriveExampleAc ) -> Result<(), ::optionable::optionable::Error> {
888                            if let Some(other_value) = other.name {
889                                self.name =  other_value;
890                            }
891                            <Option<String> as ::optionable::OptionableConvert>::merge(&mut self.middle_name, other.middle_name)?;
892                            if let Some(other_value) = other.surname {
893                                self.surname =  other_value;
894                            }
895                            Ok(())
896                        }
897                    }
898                },
899            },
900            // named struct fields with forwarded derives and Serialize annotations (full path variant)
901            TestCase {
902                input: quote! {
903                    #[derive(Optionable)]
904                    #[optionable(derive(serde::Deserialize,serde::Serialize),suffix="Ac")]
905                    struct DeriveExample {
906                        name: String,
907                        surname: String,
908                    }
909                },
910                output: quote! {
911                    #[automatically_derived]
912                    #[derive(serde::Deserialize, serde::Serialize)]
913                    struct DeriveExampleAc {
914                        #[serde(skip_serializing_if = "Option::is_none")]
915                        name: Option<String>,
916                        #[serde(skip_serializing_if = "Option::is_none")]
917                        surname: Option<String>
918                    }
919
920                    #[automatically_derived]
921                    impl ::optionable::Optionable for DeriveExample {
922                        type Optioned = DeriveExampleAc;
923                    }
924
925                    #[automatically_derived]
926                    impl ::optionable::Optionable for DeriveExampleAc {
927                        type Optioned = DeriveExampleAc;
928                    }
929
930                    #[automatically_derived]
931                    impl ::optionable::OptionableConvert for DeriveExample {
932                        fn into_optioned (self) -> DeriveExampleAc {
933                            DeriveExampleAc {
934                                name: Some(self.name),
935                                surname: Some(self.surname)
936                            }
937                        }
938
939                        fn try_from_optioned(value: DeriveExampleAc ) -> Result <Self, ::optionable::optionable::Error> {
940                             Ok(Self{
941                                name: value.name.ok_or(optionable::optionable::Error{ missing_fields: std::vec!["name"]})?,
942                                surname: value.surname.ok_or(optionable::optionable::Error{ missing_fields: std::vec!["surname"]})?
943                            })
944                        }
945
946                        fn merge(&mut self, other: DeriveExampleAc ) -> Result<(), ::optionable::optionable::Error> {
947                            if let Some(other_value) = other.name {
948                                self.name = other_value;
949                            }
950                            if let Some(other_value) = other.surname {
951                                self.surname = other_value;
952                            }
953                            Ok(())
954                        }
955                    }
956                },
957            },
958            // unnamed struct fields
959            TestCase {
960                input: quote! {
961                    #[derive(Optionable)]
962                    struct DeriveExample(pub String, i32);
963                },
964                output: quote! {
965                    #[automatically_derived]
966                    struct DeriveExampleOpt(
967                        pub Option<String>,
968                        Option<i32>
969                    );
970
971                    #[automatically_derived]
972                    impl ::optionable::Optionable for DeriveExample {
973                        type Optioned = DeriveExampleOpt;
974                    }
975
976                    #[automatically_derived]
977                    impl ::optionable::Optionable for DeriveExampleOpt {
978                        type Optioned = DeriveExampleOpt;
979                    }
980
981                    #[automatically_derived]
982                    impl ::optionable::OptionableConvert for DeriveExample {
983                        fn into_optioned (self) -> DeriveExampleOpt {
984                            DeriveExampleOpt (
985                                Some(self.0),
986                                Some(self.1)
987                            )
988                        }
989
990                         fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::optionable::Error> {
991                            Ok(Self(
992                                value.0.ok_or(optionable::optionable::Error { missing_fields: std::vec!["0"] })?,
993                                value.1.ok_or(optionable::optionable::Error { missing_fields: std::vec!["1"] })?
994                            ))
995                        }
996
997                        fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::optionable::Error> {
998                            if let Some(other_value) = other.0 {
999                                self.0 = other_value;
1000                            }
1001                            if let Some (other_value) = other.1 {
1002                                self.1 = other_value;
1003                            }
1004                            Ok (())
1005                        }
1006                    }
1007                },
1008            },
1009            // unnamed struct fields with required
1010            TestCase {
1011                input: quote! {
1012                    #[derive(Optionable)]
1013                    struct DeriveExample(pub String, #[optionable(required)] i32);
1014                },
1015                output: quote! {
1016                    #[automatically_derived]
1017                    struct DeriveExampleOpt(
1018                        pub Option<String>,
1019                        i32
1020                    );
1021
1022                    #[automatically_derived]
1023                    impl ::optionable::Optionable for DeriveExample {
1024                        type Optioned = DeriveExampleOpt;
1025                    }
1026
1027                    #[automatically_derived]
1028                    impl ::optionable::Optionable for DeriveExampleOpt {
1029                        type Optioned = DeriveExampleOpt;
1030                    }
1031
1032                    # [automatically_derived]
1033                    impl ::optionable::OptionableConvert for DeriveExample {
1034                        fn into_optioned (self) -> DeriveExampleOpt {
1035                            DeriveExampleOpt (
1036                                Some(self.0),
1037                                self.1
1038                            )
1039                        }
1040
1041                         fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::optionable::Error> {
1042                            Ok(Self(
1043                                value.0.ok_or(optionable::optionable::Error { missing_fields: std::vec!["0"] })?,
1044                                value.1))
1045                        }
1046
1047                        fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::optionable::Error> {
1048                            if let Some(other_value) = other.0 {
1049                                self.0 = other_value;
1050                            }
1051                            self.1 = other.1;
1052                            Ok (())
1053                        }
1054                    }
1055                },
1056            },
1057            // named struct fields with generics
1058            TestCase {
1059                input: quote! {
1060                    #[derive(Optionable)]
1061                    struct DeriveExample<T, T2: Serialize> where T: DeserializeOwned {
1062                        output: T,
1063                        input: T2,
1064                    }
1065                },
1066                output: quote! {
1067                    #[automatically_derived]
1068                    struct DeriveExampleOpt<T, T2: Serialize>
1069                        where T: DeserializeOwned + ::optionable::Optionable,
1070                              T2: ::optionable::Optionable {
1071                        output: Option< <T as ::optionable::Optionable>::Optioned>,
1072                        input: Option< <T2 as ::optionable::Optionable>::Optioned>
1073                    }
1074
1075                    #[automatically_derived]
1076                    impl<T, T2: Serialize> ::optionable::Optionable for DeriveExample<T, T2>
1077                        where T: DeserializeOwned + ::optionable::Optionable,
1078                              T2: ::optionable::Optionable  {
1079                        type Optioned = DeriveExampleOpt<T,T2>;
1080                    }
1081
1082                    #[automatically_derived]
1083                    impl<T, T2: Serialize> ::optionable::Optionable for DeriveExampleOpt<T, T2>
1084                        where T: DeserializeOwned + ::optionable::Optionable,
1085                              T2: ::optionable::Optionable  {
1086                        type Optioned = DeriveExampleOpt<T,T2>;
1087                    }
1088
1089                    #[automatically_derived]
1090                    impl <T, T2:Serialize> ::optionable::OptionableConvert for DeriveExample<T, T2 >
1091                        where T: DeserializeOwned + ::optionable::OptionableConvert,
1092                              T2: ::optionable::OptionableConvert {
1093                        fn into_optioned (self) -> DeriveExampleOpt<T, T2> {
1094                            DeriveExampleOpt::<T, T2> {
1095                                output: Some(<T as::optionable::OptionableConvert>::into_optioned(self.output)),
1096                                input: Some(<T2 as::optionable::OptionableConvert>::into_optioned(self.input))
1097                            }
1098                        }
1099
1100                         fn try_from_optioned(value: DeriveExampleOpt<T, T2> ) -> Result <Self, ::optionable::optionable::Error> {
1101                             Ok(Self{
1102                                output: <T as ::optionable::OptionableConvert>::try_from_optioned(value.output.ok_or(optionable::optionable::Error { missing_fields: std::vec!["output"] })?)?,
1103                                input: <T2 as ::optionable::OptionableConvert>::try_from_optioned(value.input.ok_or(optionable::optionable::Error { missing_fields: std::vec!["input"] })?)?
1104                            })
1105                        }
1106
1107                        fn merge(&mut self, other: DeriveExampleOpt<T, T2> ) -> Result<(), ::optionable::optionable::Error> {
1108                            if let Some(other_value) = other.output {
1109                                <T as ::optionable::OptionableConvert>::merge(&mut self.output, other_value)?;
1110                            }
1111                            if let Some(other_value) = other.input {
1112                                <T2 as ::optionable::OptionableConvert>::merge(&mut self.input, other_value)?;
1113                            }
1114                            Ok(())
1115                        }
1116                    }
1117                },
1118            },
1119            TestCase {
1120                input: quote! {
1121                    #[derive(Optionable)]
1122                    enum DeriveExample {
1123                        Unit,
1124                        Plain(String),
1125                        Address{street: String, number: u32},
1126                        Address2(String,u32),
1127                    }
1128                },
1129                output: quote! {
1130                    # [automatically_derived]
1131                    enum DeriveExampleOpt {
1132                        Unit,
1133                        Plain( Option<String> ),
1134                        Address{ street: Option<String>, number:Option<u32> },
1135                        Address2( Option<String>, Option<u32> )
1136                    }
1137
1138                    #[automatically_derived]
1139                    impl ::optionable::Optionable for DeriveExample {
1140                        type Optioned = DeriveExampleOpt;
1141                    }
1142
1143                    #[automatically_derived]
1144                    impl ::optionable::Optionable for DeriveExampleOpt {
1145                        type Optioned = DeriveExampleOpt;
1146                    }
1147
1148                    #[automatically_derived]
1149                    impl ::optionable::OptionableConvert for DeriveExample {
1150                        fn into_optioned (self) -> DeriveExampleOpt {
1151                            match self{
1152                                Self::Unit => DeriveExampleOpt::Unit,
1153                                Self::Plain(self_0) => DeriveExampleOpt::Plain(
1154                                    Some(self_0)
1155                                ),
1156                                Self::Address{street: self_street, number: self_number} => DeriveExampleOpt::Address{
1157                                    street: Some(self_street),
1158                                    number: Some(self_number)
1159                                },
1160                                Self::Address2(self_0, self_1) => DeriveExampleOpt::Address2(
1161                                    Some(self_0),
1162                                    Some(self_1)
1163                                )
1164                            }
1165                        }
1166
1167                         fn try_from_optioned(other: DeriveExampleOpt) -> Result <Self, ::optionable::optionable::Error> {
1168                            Ok (match other {
1169                                DeriveExampleOpt::Unit => Self::Unit,
1170                                DeriveExampleOpt::Plain(other_0) => Self::Plain(
1171                                    other_0.ok_or(optionable::optionable::Error { missing_fields: std::vec!["0"] })?
1172                                ),
1173                                DeriveExampleOpt::Address{street: other_street, number: other_number} => Self::Address{
1174                                    street: other_street.ok_or(optionable::optionable::Error { missing_fields: std::vec!["street"] })?,
1175                                    number: other_number.ok_or(optionable::optionable::Error { missing_fields: std::vec!["number"] })?
1176                                },
1177                                DeriveExampleOpt::Address2(other_0, other_1) => Self::Address2(
1178                                    other_0.ok_or(optionable::optionable::Error { missing_fields: std::vec!["0"] })?,
1179                                    other_1.ok_or(optionable::optionable::Error { missing_fields: std::vec! ["1"] })?)
1180                            })
1181                        }
1182
1183                        fn merge(&mut self, other: DeriveExampleOpt) -> Result<(), ::optionable::optionable::Error> {
1184                            match other {
1185                                DeriveExampleOpt::Unit => {
1186                                    if let Self::Unit = self {} else {
1187                                        *self = Self::try_from_optioned(DeriveExampleOpt::Unit)?;
1188                                    }
1189                                },
1190                                DeriveExampleOpt::Plain(other_0) => {
1191                                    if let Self::Plain(self_0) = self{
1192                                        if let Some(other_value) = other_0 {
1193                                            *self_0 = other_value;
1194                                        }
1195                                    } else {
1196                                        *self = Self::try_from_optioned(DeriveExampleOpt::Plain(other_0))?;
1197                                    }
1198                                },
1199                                DeriveExampleOpt::Address{street: other_street, number: other_number} => {
1200                                    if let Self::Address{street: self_street, number: self_number}  = self{
1201                                        if let Some(other_value) = other_street {
1202                                            *self_street = other_value;
1203                                        }
1204                                        if let Some(other_value) = other_number {
1205                                            *self_number = other_value;
1206                                        }
1207                                    } else {
1208                                        *self = Self::try_from_optioned(DeriveExampleOpt::Address{street: other_street, number: other_number})?;
1209                                    }
1210                                },
1211                                DeriveExampleOpt::Address2(other_0, other_1) => {
1212                                    if let Self::Address2(self_0, self_1) = self{
1213                                        if let Some(other_value) = other_0 {
1214                                            *self_0 = other_value;
1215                                        }
1216                                        if let Some(other_value) = other_1 {
1217                                            *self_1 = other_value;
1218                                        }
1219                                    } else {
1220                                        *self = Self::try_from_optioned(DeriveExampleOpt::Address2(other_0, other_1))?;
1221                                    }
1222                                }
1223                            }
1224                            Ok(())
1225                        }
1226                    }
1227                },
1228            },
1229        ];
1230        for tc in tcs {
1231            let input = syn::parse2(tc.input).unwrap();
1232            let output = derive_optionable(input).unwrap();
1233            println!("{output}");
1234            assert_eq!(tc.output.to_string(), output.to_string());
1235        }
1236    }
1237}