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