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.
12
13mod helper;
14mod k8s_openapi;
15mod parsed_input;
16mod where_clause;
17
18use crate::helper::{destructure, error, error_on_helper_attributes, is_serialize, struct_wrapper};
19use crate::k8s_openapi::{
20    error_missing_features, k8s_adjust_fields, k8s_derives, k8s_openapi_impl_metadata,
21    k8s_openapi_impl_resource, k8s_resource_type, k8s_type_attr,
22};
23use crate::parsed_input::{
24    into_field_handling, FieldHandling, FieldParsed, StructParsed, StructType,
25};
26use crate::where_clause::{where_clauses, WhereClauses};
27use darling::util::PathList;
28use darling::{FromAttributes, FromDeriveInput, FromMeta};
29use itertools::MultiUnzip;
30use proc_macro2::{Ident, Literal, TokenStream};
31use quote::{format_ident, quote, ToTokens};
32use std::borrow::Cow;
33use std::collections::{BTreeSet, HashMap, HashSet};
34use std::default::Default;
35use std::mem::take;
36use syn::parse::Parser;
37use syn::punctuated::Punctuated;
38use syn::spanned::Spanned;
39use syn::{
40    parse_quote, Attribute, Data, DeriveInput, Error, Field, Fields, LitStr, Meta, MetaList, Path, Token,
41    Type, TypePath, WhereClause,
42};
43
44const HELPER_IDENT: &str = "optionable";
45const HELPER_ATTR_IDENT: &str = "optionable_attr";
46const ERR_MSG_HELPER_ATTR_ENUM_VARIANTS: &str =
47    "#[optionable] helper attributes not supported on enum variant level.";
48
49#[derive(FromDeriveInput)]
50#[darling(attributes(optionable))]
51/// Helper attributes on the type definition level (attached to the `struct` or `enum` itself).
52pub(crate) struct TypeHelperAttributes {
53    /// Forwards the specified `attr` to the definition of the optioned type.
54    /// If any `key` is set it filters the configuration of the forwarded attributes for the specified configuration names.
55    /// E.g. `#[optionable(attr_copy(attr=serde,key=rename)]` will only forward the `rename`sub-attribute of serde.
56    #[darling(multiple)]
57    attr_copy: Vec<FieldAttributeToCopy>,
58    /// Derive-macros that should be added to the optioned type
59    #[darling(multiple)]
60    derive: Vec<PathList>,
61    /// Explicit suffix to use for the optioned type.
62    suffix: Option<LitStr>,
63    /// Skip generating `OptionableConvert` impl
64    no_convert: Option<()>,
65    /// Adjustments of the derived optioned type for fields that use the `k8s_openapi` serialization replacements.
66    /// - Derives for the optioned type `Clone, Debug, PartialEq, Serialize, Deserialize` and additionally for Structs `Default`.
67    /// - Takes the `k8s_openapi` serialization replacements into account for its own serialization.
68    k8s_openapi: Option<TypeHelperAttributesK8sOpenapi>,
69    /// Adjustments of the derived optioned type for structs that `kube::CustomResource` or respective subfields.
70    /// - Derives for the optioned type `Clone, Debug, PartialEq, Serialize, Deserialize` and additionally for Structs `Default`.
71    /// - Sets `#[serde(rename_all="camelCase",deny_unknown_fields)]`
72    /// - Copy `#[(serde(rename="...")]` and `#[(serde(rename_all="...")]` attributes over to the optioned types.
73    kube: Option<TypeHelperAttributesKube>,
74}
75
76#[derive(FromMeta)]
77/// Adjustments of the derived optioned type for fields that use the `k8s_openapi` serialization replacements.
78/// - Derives for the optioned type `Clone, Debug, PartialEq, Serialize, Deserialize` and additionally for Structs `Default`.
79/// -
80/// - Takes the `k8s_openapi` serialization replacements into account for its own serialization.
81pub(crate) struct TypeHelperAttributesK8sOpenapi {
82    /// Adjustments of the derived optioned type for `k8s_openapi::Metadata`-implementations.
83    /// - Sets the `metadata` field as required for the optioned type.
84    /// - Derives `k8s_openapi::Metadata` for the optioned type.
85    metadata: Option<()>,
86    /// Adjustments of the derived optioned type for `k8s_openapi::Resource`-implementations.
87    /// - Adds `apiVersion` and `kind` to the serialization output with values from the trait constants of the given `k8s_openapi::Resource` implementation
88    /// - Derives `k8s_openapi::resource` for the optioned type.
89    resource: Option<()>,
90}
91
92#[derive(FromMeta)]
93/// Adjustments of the derived optioned type for structs that `kube::CustomResource` or respective subfields.
94/// - Derives for the optioned type `Clone, Debug, PartialEq, Serialize, Deserialize` and additionally for Structs `Default`.
95pub(crate) struct TypeHelperAttributesKube {
96    /// Adjustments of the derived optioned type for `kube::Resource`-implementations.
97    /// - Sets the `metadata` field as required for the optioned type.
98    /// - Adds `apiVersion` and `kind` to the serialization output with values from the trait constants of the given `kube::Resource` implementation
99    /// - Derives `kube::Resource` for the optioned type.
100    resource: Option<()>,
101}
102
103#[derive(FromMeta)]
104/// Forwards the specified `attr` to the definition of the optioned type.
105/// If `sub_names` is set it filters the configuration of the forwarded attribure for the specified configuration names.
106/// E.g. `attr=serde,sub_names=rename` will only forward the `rename`sub-attribute of serde.
107pub struct FieldAttributeToCopy {
108    pub attr: Path,
109    pub key: Option<Path>,
110}
111
112#[derive(FromDeriveInput, Debug, Clone)]
113#[darling(attributes(optionable))]
114/// Settings that are only available to be set via the rust function signature of `derive_optionable`
115/// but not via the derive macro (as they are not useful in that context and would lead to an
116/// increased but not useful API surface of the derive macro helpers).
117pub struct CodegenSettings {
118    /// Name of the optionable crate to use for the generated code including potentially
119    /// leading `::`. Useful to set when generating code for the `optionable`
120    /// crate itself where one needs to refer to it as `crate`.
121    pub optionable_crate_name: Path,
122    /// Path prefix to prepend to the respective type. E.g. with a `ty_prefix` of `::mycrate`
123    /// the output would be (simplified) `impl Optionable for ::mycrate::mytype`.
124    pub ty_prefix: Option<Path>,
125    /// Replacement for the keyword `crate` in the input type/enum definition or references.
126    /// Useful when generating code for the `optionable` crate as pre-existing `crate` references
127    /// need to be replaced with the concret crate name.
128    pub input_crate_replacement: Option<Ident>,
129}
130
131/// Processes the input field attribute forwards to a `HashMap`.
132/// Adds `attr=serde,key=rename` if the `kube` input attribute is present.
133fn field_attr_copy_hashmap(
134    input: Vec<FieldAttributeToCopy>,
135    attr_kube: Option<&TypeHelperAttributesKube>,
136) -> HashMap<Path, HashSet<Path>> {
137    let mut result = HashMap::<Path, HashSet<Path>>::new();
138    for el in input {
139        if let Some(key) = el.key {
140            if let Some(entry) = result.get_mut(&el.attr) {
141                entry.insert(key);
142            } else {
143                result.insert(el.attr, HashSet::from([key]));
144            }
145        }
146    }
147    if attr_kube.is_some() {
148        let path_serde = Path::from_string("serde").unwrap();
149        let path_rename = Path::from_string("rename").unwrap();
150        let path_rename_all = Path::from_string("rename_all").unwrap();
151        let path_rename_all_fields = Path::from_string("rename_all_fields").unwrap();
152        if let Some(entry) = result.get_mut(&path_serde) {
153            // If the keys are empty everything will be copied, so don't restrict it here
154            if !entry.is_empty() {
155                entry.insert(path_rename);
156                entry.insert(path_rename_all);
157            }
158        } else {
159            result.insert(
160                path_serde,
161                HashSet::from([path_rename, path_rename_all, path_rename_all_fields]),
162            );
163        }
164    }
165    result
166}
167
168impl Default for CodegenSettings {
169    fn default() -> Self {
170        Self {
171            optionable_crate_name: parse_quote!(::optionable),
172            ty_prefix: None,
173            input_crate_replacement: None,
174        }
175    }
176}
177
178#[derive(FromAttributes)]
179#[darling(attributes(optionable))]
180/// Helper attributes on the type definition level (attached to the `struct` or `enum` itself).
181struct FieldHelperAttributes {
182    /// Given field won't be optioned, it will also be required for the derived optioned type.
183    required: Option<()>,
184    /// For circumvention of the orphan rule. Uses the provided type for the optioned variant of the field.
185    /// If `no_convert` is not set the provided type has to implement `OptionedConvert<O>` where `O` is the type of this field in the original type.
186    #[darling(rename = "optioned_type")]
187    optioned_ty: Option<Path>,
188}
189
190/// Returns the attribute for opting-out of `OptionableConvert`-impl generation.
191#[must_use]
192pub fn attribute_no_convert() -> Attribute {
193    parse_quote!(#[optionable(no_convert)])
194}
195
196/// Returns the attribute for setting a custom struct/enum name suffix instead of `Opt`.
197#[must_use]
198pub fn attribute_suffix(suffix: &str) -> Attribute {
199    parse_quote!(#[optionable(suffix=#suffix)])
200}
201
202/// Returns the attribute for setting that the given identifiers should be added as
203/// derive macro to the optioned struct/enum.
204#[must_use]
205pub fn attribute_derives(derives: &PathList) -> Attribute {
206    parse_quote!(#[optionable(derive(#(#derives),*))])
207}
208
209/// Derives the `Optionable`-trait from the main `optionable`-library.
210///
211/// # Errors
212/// - on misplaced helper attributes
213/// - internal implementation errors
214#[allow(clippy::too_many_lines)]
215#[allow(clippy::items_after_statements)]
216pub fn derive_optionable(
217    input: DeriveInput,
218    settings: Option<&CodegenSettings>,
219) -> syn::Result<TokenStream> {
220    let settings = settings.map(Cow::Borrowed).unwrap_or_default();
221    let crate_name = &settings.optionable_crate_name;
222    let TypeHelperAttributes {
223        attr_copy,
224        derive: attr_derive,
225        suffix: attr_suffix,
226        no_convert: attr_no_convert,
227        k8s_openapi: attr_k8s_openapi,
228        kube: attr_kube,
229    } = TypeHelperAttributes::from_derive_input(&input)?;
230    let DeriveInput {
231        attrs,
232        vis,
233        mut generics,
234        data,
235        ..
236    } = input;
237    error_missing_features(attr_k8s_openapi.as_ref(), attr_kube.as_ref())?;
238
239    let ty_ident_opt = {
240        let suffix = attr_suffix.map_or_else(
241            || {
242                if attr_kube.is_some() || attr_k8s_openapi.is_some() {
243                    Cow::Borrowed("Ac")
244                } else {
245                    Cow::Borrowed("Opt")
246                }
247            },
248            |s| Cow::Owned(s.value()),
249        );
250        Ident::new(&(input.ident.to_string() + &suffix), input.ident.span())
251    };
252    let ty_ident = if let Some(mut ty_prefix) = settings.ty_prefix.clone() {
253        ty_prefix.segments.push(input.ident.into());
254        ty_prefix
255    } else {
256        input.ident.into()
257    };
258    if let Data::Enum(e) = &data
259        && e.variants
260            .iter()
261            .all(|el| matches!(el.fields, Fields::Unit))
262    {
263        // plain enum without nested structs/tuples
264        return Ok(impl_optionable_self(
265            crate_name,
266            &ty_ident,
267            attr_no_convert.is_some(),
268        ));
269    }
270
271    let k8s_resource_type = k8s_resource_type(attr_k8s_openapi.as_ref(), attr_kube.as_ref())?;
272    let k8s_openapi_attrs = attr_k8s_openapi.is_some().then(|| k8s_type_attr(&data));
273    let attr_copy_identifier = field_attr_copy_hashmap(attr_copy, attr_kube.as_ref());
274    let ty_attr_forwarded = forwarded_attributes(&attrs, &attr_copy_identifier)?;
275
276    let mut derive = attr_derive
277        .into_iter()
278        .flat_map(|el| {
279            el.iter()
280                .map(|el| el.to_token_stream().to_string())
281                .collect::<Vec<_>>()
282        })
283        .collect::<BTreeSet<_>>();
284    if (attr_k8s_openapi.is_some() || attr_kube.is_some())
285        && let Some(k8s_derives) = k8s_derives(&data)
286    {
287        for el in k8s_derives {
288            derive.insert(el);
289        }
290    }
291
292    let vis = vis;
293    // basically split_for_impl, but we move the where clause out instead of just taking a reference
294    let where_clause = take(&mut generics.where_clause);
295    let (impl_generics, ty_generics, _) = generics.split_for_impl();
296    let generics_colon = (!generics.params.is_empty()).then(|| quote! {::});
297    let skip_optionable_if_serde_serialize =
298        (attr_k8s_openapi.is_some() // also sets #[derive(Serialize)]
299            || derive.iter().any(|el| is_serialize(el.as_str())))
300        .then(|| quote!(#[serde(skip_serializing_if = "Option::is_none")]));
301
302    /// Helper to collect the enum/struct specific derived code aspects in a typesafe way
303    struct Derived {
304        /// Either `enum` or `struct`
305        enum_struct: TokenStream,
306        /// The generated fields, including wrapping brackets
307        fields: TokenStream,
308        /// The `where`-clause for the `struct_enum` definition
309        where_clause_struct_enum: WhereClause,
310        /// The `where`-clause for the `Optionable`-impl
311        where_clause_impl_optionable: WhereClause,
312        /// The `where`-clause  for the `OptionableConvert` and `OptionedConvert` impl
313        where_clause_impl_optionable_convert: Option<WhereClause>,
314        /// The `OptionableConvert` implementation if not configured to be skipped, including the `impl`-wrapper
315        impl_optionable_convert: Option<TokenStream>,
316    }
317    let Derived {
318        enum_struct,
319        fields,
320        where_clause_struct_enum,
321        where_clause_impl_optionable,
322        where_clause_impl_optionable_convert,
323        impl_optionable_convert,
324    } = match data {
325        Data::Struct(s) => {
326            let mut struct_parsed = into_field_handling(
327                crate_name.to_owned(),
328                s.fields,
329                settings.input_crate_replacement.as_ref(),
330            )?;
331            k8s_adjust_fields(
332                &mut struct_parsed,
333                attr_k8s_openapi.as_ref(),
334                attr_kube.as_ref(),
335                k8s_resource_type.as_ref(),
336                crate_name,
337            )?;
338            let WhereClauses {
339                struct_enum_def: where_clause_struct_enum,
340                impl_optionable: where_clause_impl_optionable,
341                impl_optionable_convert: where_clause_impl_optionable_convert,
342            } = where_clauses(
343                where_clause,
344                &generics.params,
345                crate_name,
346                settings.input_crate_replacement.as_ref(),
347                &derive,
348                attr_no_convert.is_some(),
349                &struct_parsed.fields,
350            )?;
351            let unnamed_struct_semicolon =
352                (struct_parsed.struct_type == StructType::Unnamed).then(|| quote!(;));
353            let optioned_fields = optioned_fields(
354                &struct_parsed,
355                skip_optionable_if_serde_serialize.as_ref(),
356                &attr_copy_identifier,
357            )?;
358
359            let impl_optionable_convert = attr_no_convert.is_none().then(|| {
360                let into_optioned_fields = into_optioned(&struct_parsed, |selector| quote! { self.#selector });
361                let try_from_optioned_fields =
362                    try_from_optioned(&struct_parsed, |selector| quote! { value.#selector });
363                let merge_fields = merge_fields(
364                    &struct_parsed,
365                    |selector| quote! { self.#selector },
366                    |selector| quote! { other.#selector },
367                    true);
368                quote! {
369                    #[automatically_derived]
370                    impl #impl_generics #crate_name::OptionableConvert for #ty_ident #ty_generics #where_clause_impl_optionable_convert{
371                        fn into_optioned(self) -> #ty_ident_opt #ty_generics {
372                            #ty_ident_opt #generics_colon #ty_generics #into_optioned_fields
373                        }
374
375                        fn try_from_optioned(value: #ty_ident_opt #ty_generics) -> Result<Self, #crate_name::Error>{
376                            Ok(Self #try_from_optioned_fields)
377                        }
378
379                        fn merge(&mut self, other: #ty_ident_opt #ty_generics) -> Result<(), #crate_name::Error>{
380                            #merge_fields
381                            Ok(())
382                        }
383                    }
384                }
385            });
386            Derived {
387                enum_struct: quote! {struct},
388                fields: quote! {#optioned_fields #unnamed_struct_semicolon},
389                where_clause_struct_enum,
390                where_clause_impl_optionable,
391                where_clause_impl_optionable_convert,
392                impl_optionable_convert,
393            }
394        }
395        Data::Enum(e) => {
396            let self_prefix = quote! {self_};
397            let other_prefix = quote! {other_};
398
399            let variants = e
400                .variants
401                .into_iter()
402                .map(|v| {
403                    error_on_helper_attributes(&v.attrs, ERR_MSG_HELPER_ATTR_ENUM_VARIANTS)?;
404                    let mut field_handling = into_field_handling(
405                        crate_name.to_owned(),
406                        v.fields,
407                        settings.input_crate_replacement.as_ref(),
408                    )?;
409                    k8s_adjust_fields(
410                        &mut field_handling,
411                        attr_k8s_openapi.as_ref(),
412                        attr_kube.as_ref(),
413                        k8s_resource_type.as_ref(),
414                        crate_name,
415                    )?;
416                    Ok::<_, Error>((
417                        v.ident,
418                        forwarded_attributes(&v.attrs, &attr_copy_identifier)?,
419                        field_handling,
420                    ))
421                })
422                .collect::<Result<Vec<_>, _>>()?;
423            let all_fields = variants
424                .iter()
425                .flat_map(|(_, _, fields)| &fields.fields)
426                .collect::<Vec<_>>();
427            let WhereClauses {
428                struct_enum_def: where_clause_struct_enum,
429                impl_optionable: where_clause_impl_optionable,
430                impl_optionable_convert: where_clause_impl_optionable_convert,
431            } = where_clauses(
432                where_clause,
433                &generics.params,
434                crate_name,
435                settings.input_crate_replacement.as_ref(),
436                &derive,
437                attr_no_convert.is_some(),
438                all_fields,
439            )?;
440
441            let optioned_variants = variants
442                .iter()
443                .map(|(variant, forward_attrs, f)| {
444                    let fields = optioned_fields(
445                        f,
446                        skip_optionable_if_serde_serialize.as_ref(),
447                        &attr_copy_identifier,
448                    )?;
449                    Ok::<_, Error>(quote!( #forward_attrs #variant #fields ))
450                })
451                .collect::<Result<Vec<_>, _>>()?;
452
453            let impl_optionable_convert = attr_no_convert.is_none().then(|| {
454                let (into_variants, try_from_variants, merge_variants): (Vec<_>, Vec<_>, Vec<_>) = variants
455                    .iter()
456                    .map(|(variant, _, struct_parsed)| {
457                        let fields_into = into_optioned(struct_parsed, |selector| {
458                            format_ident!("{self_prefix}{selector}").to_token_stream()
459                        });
460                        let fields_try_from = try_from_optioned
461                            (struct_parsed, |selector| {
462                                format_ident!("{other_prefix}{selector}").to_token_stream()
463                            });
464                        let fields_merge = merge_fields(struct_parsed,
465                                                        |selector| format_ident!("{self_prefix}{selector}").to_token_stream(),
466                                                        |selector| format_ident!("{other_prefix}{selector}").to_token_stream(),
467                                                        false);
468                        let self_destructure = destructure(struct_parsed, &self_prefix)?;
469                        let other_destructure = destructure(struct_parsed, &other_prefix)?;
470                        Ok::<_, Error>((
471                            quote!( Self::#variant #self_destructure => #ty_ident_opt::#variant #fields_into ),
472                            quote!( #ty_ident_opt::#variant #other_destructure => Self::#variant #fields_try_from ),
473                            quote!( #ty_ident_opt::#variant #other_destructure => {
474                                if let Self::#variant #self_destructure = self {
475                                    #fields_merge
476                                } else {
477                                    *self = Self::try_from_optioned(#ty_ident_opt::#variant #other_destructure)?;
478                                }
479                            })
480                        ))
481                    })
482                    .collect::<Result<Vec<_>, _>>()?
483                    .into_iter().multiunzip();
484                Ok::<_, Error>(quote! {
485                    #[automatically_derived]
486                    impl #impl_generics #crate_name::OptionableConvert for #ty_ident #ty_generics #where_clause_impl_optionable_convert {
487                        fn into_optioned(self) -> #ty_ident_opt #ty_generics {
488                            match self {
489                                #(#into_variants),*
490                            }
491                        }
492
493                        fn try_from_optioned(other: #ty_ident_opt #ty_generics)->Result<Self,#crate_name::Error>{
494                            Ok(match other{
495                                #(#try_from_variants),*
496                            })
497                        }
498
499                        fn merge(&mut self, other: #ty_ident_opt #ty_generics) -> Result<(), #crate_name::Error>{
500                            match other{
501                                #(#merge_variants),*
502                            }
503                            Ok(())
504                        }
505                    }
506                })
507            }).transpose()?;
508            Derived {
509                enum_struct: quote! {enum},
510                fields: quote! {{#(#optioned_variants),*}},
511                where_clause_struct_enum,
512                where_clause_impl_optionable,
513                where_clause_impl_optionable_convert,
514                impl_optionable_convert,
515            }
516        }
517        Data::Union(_) => return error("#[derive(Optionable)] not supported for unit structs"),
518    };
519    let impl_optioned_convert = attr_no_convert.is_none().then(|| {
520        quote! {
521            #[automatically_derived]
522            impl #impl_generics #crate_name::OptionedConvert<#ty_ident #ty_generics> for #ty_ident_opt #ty_generics #where_clause_impl_optionable_convert {
523                fn from_optionable(value: #ty_ident #ty_generics) -> Self {
524                    #crate_name::OptionableConvert::into_optioned(value)
525                }
526                fn try_into_optionable(self) -> Result<#ty_ident #ty_generics, #crate_name::Error> {
527                    #crate_name::OptionableConvert::try_from_optioned(self)
528                }
529                fn merge_into(self, other: &mut #ty_ident #ty_generics) -> Result<(), #crate_name::Error> {
530                    #crate_name::OptionableConvert::merge(other, self)
531                }
532            }
533        }
534    });
535
536    let derive = derive
537        .into_iter()
538        .map(|derive| Path::from_string(&derive))
539        .collect::<Result<Vec<_>, _>>()?;
540    let derive = (!derive.is_empty()).then(|| quote! {#[derive(#(#derive),*)]});
541
542    let k8s_openapi_impl_resource = attr_k8s_openapi
543        .as_ref()
544        .is_some_and(|attr| attr.resource.is_some())
545        .then(|| {
546            k8s_openapi_impl_resource(
547                &ty_ident,
548                &ty_ident_opt,
549                &impl_generics,
550                &ty_generics,
551                &where_clause_impl_optionable,
552            )
553        });
554    let impl_k8s_metadata = attr_k8s_openapi
555        .as_ref()
556        .is_some_and(|attr| attr.metadata.is_some())
557        .then(|| {
558            k8s_openapi_impl_metadata(
559                &ty_ident,
560                &ty_ident_opt,
561                &impl_generics,
562                &ty_generics,
563                &where_clause_impl_optionable,
564            )
565        });
566    let kube_derive_resource = attr_kube
567        .is_some_and(|attr| attr.resource.is_some())
568        .then(|| {
569            quote! {
570                #[derive(kube::Resource)]
571                #[resource(inherit = #ty_ident )]
572            }
573        });
574    let k8s_roundtrip_test = (attr_k8s_openapi.is_some_and(|attr|attr.resource.is_some())
575        && ty_generics.to_token_stream().is_empty()
576        // causes stackoverflows during the roundtrip tests, the stackoverflow already happens when trying to generate a `fake CRD`, so doesn't originate from this crate
577        // (we rely on a forked version of k8s-openapi where we just added derives for `fake::Dummy`, but the CRD includes a `JSONSchemaProps` subfield which includes a lot
578        // of other subfields involving `JSONSchemaProps`. Hence, `fake` easily generates a way to complicated and depply nested random structure).
579        && ty_ident_opt!="CustomResourceDefinitionAc"
580        // the cases below cause false positive errors due to flattening of effectively nil substructs in the  optioned type
581        && ty_ident_opt!="StorageVersionAc"
582        && ty_ident_opt!="DeviceClassAc"
583        && ty_ident_opt!="ResourceClaimAc"
584        && ty_ident_opt!="ResourceClaimTemplateAc")
585        .then(|| {
586            let fn_name = Ident::from_string(
587                &("roundtrip_".to_owned()
588                    + &ty_ident_opt.to_token_stream().to_string().to_lowercase()),
589            )?;
590            Ok::<_, Error>(quote! {
591                #[cfg(test_k8s_openapi_roundtrip)]
592                #[test]
593                fn #fn_name() {
594                   crate::testutil::roundtrip_test::<#ty_ident>();
595                }
596            })
597        })
598        .transpose()?;
599    Ok(quote! {
600                #derive
601                #kube_derive_resource
602                #ty_attr_forwarded
603                #k8s_openapi_attrs
604                #vis #enum_struct #ty_ident_opt #impl_generics #where_clause_struct_enum #fields
605
606                #[automatically_derived]
607                impl #impl_generics #crate_name::Optionable for #ty_ident #ty_generics #where_clause_impl_optionable {
608                    type Optioned = #ty_ident_opt #ty_generics;
609                }
610
611                #[automatically_derived]
612                impl #impl_generics #crate_name::Optionable for #ty_ident_opt #ty_generics #where_clause_impl_optionable {
613                    type Optioned = #ty_ident_opt #ty_generics;
614                }
615
616                #impl_optionable_convert
617                #impl_optioned_convert
618
619                #k8s_openapi_impl_resource
620                #impl_k8s_metadata
621
622                #k8s_roundtrip_test
623    })
624}
625
626/// Returns the `Optionable` and `OptionableConvert` implementation for self-resolving types
627fn impl_optionable_self(crate_name: &Path, ty_ident: &Path, no_convert: bool) -> TokenStream {
628    let convert_impl = (!no_convert).then(|| {
629        quote! {
630            #[automatically_derived]
631            impl #crate_name::OptionableConvert for #ty_ident{
632                fn into_optioned(self) -> #ty_ident {
633                    self
634                }
635
636                fn try_from_optioned(value: Self::Optioned) -> Result<Self, #crate_name::Error> {
637                    Ok(value)
638                }
639
640                fn merge(&mut self, other: Self::Optioned) -> Result<(), #crate_name::Error> {
641                    *self = other;
642                    Ok(())
643                }
644            }
645        }
646    });
647    quote! {
648        #[automatically_derived]
649        impl #crate_name::Optionable for #ty_ident{
650            type Optioned = Self;
651        }
652
653        #convert_impl
654    }
655}
656
657/// Returns a tokenstream for the optioned fields and potential convert implementation of the optioned object (struct/enum variants).
658/// The returned tokenstream will be of the form `{...}` for named fields and `(...)` for unnamed fields.
659/// Does not include any leading `struct/enum` keywords or any trailing `;`.
660fn optioned_fields(
661    fields: &StructParsed,
662    serde_attributes: Option<&TokenStream>,
663    field_attr_forwards: &HashMap<Path, HashSet<Path>>,
664) -> Result<TokenStream, Error> {
665    let fields_token = fields.fields.iter().map(
666        |FieldParsed {
667             field: Field { attrs, vis, ident, ty, .. },
668             handling,
669         }| {
670            let forwarded_attrs = forwarded_attributes(attrs, field_attr_forwards)?;
671            let optioned_ty = optioned_ty(&fields.crate_name, ty);
672            let colon = ident.as_ref().map(|_| quote! {:});
673            Ok::<_, Error>(match handling {
674                FieldHandling::Required | FieldHandling::OptionedOnly => quote! {#forwarded_attrs #vis #ident #colon #ty},
675                FieldHandling::ManualOptioned(ty_opt) => quote! {#forwarded_attrs #vis #ident #colon Option<#ty_opt>},
676                FieldHandling::IsOption => quote! {#forwarded_attrs #serde_attributes #vis #ident #colon #optioned_ty},
677                FieldHandling::Other => quote! {#forwarded_attrs #serde_attributes #vis #ident #colon Option<#optioned_ty>},
678            })
679        },
680    ).collect::<Result<Vec<_>, _>>()?;
681    Ok(struct_wrapper(fields_token, &fields.struct_type))
682}
683
684/// Returns the field mapping implementation for `into_optioned`.
685/// The returned tokenstream will be of the form `{...}` for named fields and `(...)` for unnamed fields.
686/// Does not include any leading `struct/enum` keywords or any trailing `;`.
687fn into_optioned(
688    struct_parsed: &StructParsed,
689    self_selector_fn: impl Fn(&TokenStream) -> TokenStream,
690) -> TokenStream {
691    let fields_token = struct_parsed.fields.iter().enumerate().map(|(i, FieldParsed { field: Field { ident, ty, .. }, handling })| {
692        let colon = ident.as_ref().map(|_| quote! {:});
693        let selector = ident.as_ref().map_or_else(|| {
694            let i = Literal::usize_unsuffixed(i);
695            quote! {#i}
696        }, ToTokens::to_token_stream);
697        let self_selector = self_selector_fn(&selector);
698        let crate_name = &struct_parsed.crate_name;
699        match (handling, is_self_resolving_optioned(ty)) {
700            (FieldHandling::Required, _) | (FieldHandling::IsOption, true) => quote! {#ident #colon #self_selector},
701            (FieldHandling::ManualOptioned(_), _) => quote! {#ident #colon Some(#crate_name::OptionedConvert::from_optionable(#self_selector))},
702            (FieldHandling::IsOption, false) => quote! {#ident #colon #crate_name::OptionableConvert::into_optioned(#self_selector)},
703            (FieldHandling::Other, true) => quote! {#ident #colon Some(#self_selector)},
704            (FieldHandling::Other, false) => quote! {#ident #colon Some(#crate_name::OptionableConvert::into_optioned(#self_selector))},
705            (FieldHandling::OptionedOnly, _) => quote! {#ident #colon Default::default()},
706        }
707    });
708    struct_wrapper(fields_token, &struct_parsed.struct_type)
709}
710
711/// Returns the field-mappings implementation for `try_from_optioned`.
712/// The returned tokenstream will be of the form `{...}` for named fields and `(...)` for unnamed fields.
713/// Does not include any leading `struct/enum` keywords or any trailing `;`.
714fn try_from_optioned(
715    struct_parsed: &StructParsed,
716    value_selector_fn: impl Fn(&TokenStream) -> TokenStream,
717) -> TokenStream {
718    let fields_token = struct_parsed.fields.iter().enumerate().filter_map(|(i, FieldParsed { field: Field { ident, ty, .. }, handling })| {
719        let colon = ident.as_ref().map(|_| quote! {:});
720        let selector = ident.as_ref().map_or_else(|| {
721            let i = Literal::usize_unsuffixed(i);
722            quote! {#i}
723        }, ToTokens::to_token_stream);
724        let value_selector = value_selector_fn(&selector);
725        let crate_name = &struct_parsed.crate_name;
726        match (handling, is_self_resolving_optioned(ty)) {
727            (FieldHandling::Required, _) | (FieldHandling::IsOption, true) => Some(quote! {#ident #colon value.#selector}),
728            (FieldHandling::ManualOptioned(_), _) => {
729                let selector_quoted = LitStr::new(&selector.to_string(), ident.span());
730                Some(quote! {
731                    #ident #colon #crate_name::OptionedConvert::try_into_optionable(#value_selector.ok_or(#crate_name::Error{ missing_field: #selector_quoted })?
732                    )?
733                })
734            }
735            (FieldHandling::IsOption, false) => Some(quote! {
736                #ident #colon #crate_name::OptionableConvert::try_from_optioned(
737                    #value_selector
738                )?
739            }),
740            (FieldHandling::Other, true) => {
741                let selector_quoted = LitStr::new(&selector.to_string(), ident.span());
742                Some(quote! {
743                    #ident #colon #value_selector.ok_or(#crate_name::Error{ missing_field: #selector_quoted })?
744                })
745            }
746            (FieldHandling::Other, false) => {
747                let selector_quoted = LitStr::new(&selector.to_string(), ident.span());
748                Some(quote! {
749                    #ident #colon #crate_name::OptionableConvert::try_from_optioned(#value_selector.ok_or(#crate_name::Error{ missing_field: #selector_quoted })?
750                    )?
751                })
752            }
753            (FieldHandling::OptionedOnly, _) => None,
754        }
755    });
756
757    struct_wrapper(fields_token, &struct_parsed.struct_type)
758}
759
760/// Returns the field-mappings implementation for `try_from_optioned`.
761/// The returned tokenstream will be of the form `{...}` for named fields and `(...)` for unnamed fields.
762/// Does not include any leading `struct/enum` keywords or any trailing `;`.
763///
764/// `merge_self_mut` should be true when the `self_selector` merge argument should be modified with a `& mut` on recursive calls.
765fn merge_fields(
766    struct_parsed: &StructParsed,
767    self_selector_fn: impl Fn(&TokenStream) -> TokenStream,
768    other_selector_fn: impl Fn(&TokenStream) -> TokenStream,
769    merge_self_mut: bool,
770) -> TokenStream {
771    let fields_token = struct_parsed.fields.iter().enumerate().filter_map(
772        |(
773             i,
774             FieldParsed {
775                 field: Field { ident, ty, .. },
776                 handling,
777             },
778         )| {
779            let selector = ident.as_ref().map_or_else(
780                || {
781                    let i = Literal::usize_unsuffixed(i);
782                    quote! {#i}
783                },
784                ToTokens::to_token_stream,
785            );
786            let self_merge_mut_modifier = merge_self_mut.then(|| quote! {&mut});
787            let deref_modifier = (!merge_self_mut).then(|| quote! {*});
788            let self_selector = self_selector_fn(&selector);
789            let other_selector = other_selector_fn(&selector);
790            let crate_name = &struct_parsed.crate_name;
791            match (handling, is_self_resolving_optioned(ty)) {
792                (FieldHandling::Required, _) | (FieldHandling::IsOption, true) => Some(quote! {#deref_modifier #self_selector = #other_selector;}),
793                (FieldHandling::ManualOptioned(_), _) =>  Some(quote! {
794                    if let Some(other_value)=#other_selector{
795                        #crate_name::OptionedConvert::merge_into(other_value, #self_merge_mut_modifier #self_selector)?;
796                    }
797                }),
798                (FieldHandling::IsOption, false) =>  Some(quote! {
799                    #crate_name::OptionableConvert::merge(#self_merge_mut_modifier #self_selector, #other_selector)?;
800                }),
801                (FieldHandling::Other, true) =>  Some(quote! {
802                    if let Some(other_value)=#other_selector{
803                        #deref_modifier #self_selector = other_value;
804                    }
805                }),
806                (FieldHandling::Other, false) =>  Some(quote! {
807                    if let Some(other_value)=#other_selector{
808                        #crate_name::OptionableConvert::merge(#self_merge_mut_modifier #self_selector, other_value)?;
809                    }
810                }),
811                (FieldHandling::OptionedOnly, _) =>None,
812            }
813        },
814    );
815
816    quote! {
817        #(#fields_token)*
818    }
819}
820/// Tries to resolve the optioned type analogous to what we do in the main crate.
821/// Due to limitations to macro resolving (no order guaranteed) we have to have an explicit
822/// list of well-known types and their optioned types.
823/// For now limited to self-resolving (mostly primitive) types
824fn optioned_ty(crate_name: &Path, ty: &Type) -> TokenStream {
825    if is_self_resolving_optioned(ty) {
826        ty.to_token_stream()
827    } else {
828        quote! { <#ty as #crate_name::Optionable>::Optioned }
829    }
830}
831
832const SELF_RESOLVING_TYPES: [&str; 18] = [
833    // Rust primitives don't have inner structure, https://doc.rust-lang.org/rust-by-example/primitives.html
834    "i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64", "u128", "usize", "f32",
835    "f64", "char", "bool", // Other types without inner structure
836    "String", "OsString",
837];
838
839/// Checks when it is well known that the type resolves to itself as its `Optioned`.
840/// Limited to self-resolving (mostly primitive) types.
841fn is_self_resolving_optioned(ty: &Type) -> bool {
842    if let Type::Path(TypePath { qself, path }) = &ty
843        && qself.is_none()
844        && SELF_RESOLVING_TYPES.contains(&&*path.to_token_stream().to_string())
845    {
846        true
847    } else {
848        false
849    }
850}
851
852/// Extracts the `HELPER_ATTR_IDENT` attributes, unwraps them and returns them
853/// as `TokenStream` that can be used as respective attribute.
854fn forwarded_attributes(
855    attrs: &[Attribute],
856    attr_to_copy: &HashMap<Path, HashSet<Path>>,
857) -> Result<Option<TokenStream>, Error> {
858    let forward_attrs = attrs
859        .iter()
860        .map(|attr| {
861            if attr.path().is_ident(HELPER_ATTR_IDENT) {
862                return match &attr.meta {
863                    Meta::List(MetaList { tokens, .. }) => Ok(Some(quote!(#[#tokens]))),
864                    _ => error("Only lists like `#[optionable_attr(Serialize,Deserialize)]` are supported for `optionable_attr`"),
865                };
866            }
867            let keys_to_copy = attr_to_copy.get(attr.path());
868            if let Some(keys_to_copy) = keys_to_copy {
869                // no key restrictions
870                if keys_to_copy.is_empty() {
871                    if attr.path().is_ident("derive") && let Meta::List(meta_list) = &attr.meta {
872                        let derives = Punctuated::<Path, Token![,]>::parse_terminated
873                            .parse2(meta_list.tokens.clone())?.into_iter().filter(|el| el.to_token_stream().to_string() != "Optionable" && el.to_token_stream().to_string() != "optionable::Optionable");
874                        return Ok(Some(quote! {#[derive(#(#derives,)*)]}));
875                    }
876                    Ok(Some(attr.to_token_stream()))
877                } else {
878                    match &attr.meta {
879                        Meta::Path(_) => Ok(None),
880                        Meta::NameValue(meta_name_value) => Ok(keys_to_copy.contains(&meta_name_value.path).then(|| attr.to_token_stream())),
881                        Meta::List(meta_list) => {
882                            // we support one level of nesting for a Meta::List(Meta::NameValue(..)) setup like it's used by #[serde(rename=...)]
883                            let inner_metas: Vec<TokenStream> = Punctuated::<Meta, Token![,]>::parse_terminated
884                                .parse2(meta_list.tokens.clone())?.into_iter().filter_map(|meta| {
885                                if let Meta::NameValue(meta_name_value) = meta {
886                                    keys_to_copy.contains(&meta_name_value.path).then(|| Ok::<_, Error>(meta_name_value.to_token_stream()))
887                                } else {
888                                    None
889                                }
890                            }).collect::<Result<_, _>>()?;
891                            if inner_metas.is_empty() {
892                                Ok(None)
893                            } else {
894                                let attr = Attribute {
895                                    meta: MetaList {
896                                        path: meta_list.path.clone(),
897                                        delimiter: meta_list.delimiter.clone(),
898                                        tokens: inner_metas.into_iter().collect(),
899                                    }.into(),
900                                    ..*attr
901                                };
902                                Ok(Some(attr.to_token_stream()))
903                            }
904                        }
905                    }
906                }
907            } else {
908                Ok(None)
909            }
910        })
911        .collect::<Result<Vec<_>, _>>()?
912        .into_iter()
913        .flatten()
914        .collect::<TokenStream>();
915    Ok((!forward_attrs.is_empty()).then_some(forward_attrs))
916}
917
918#[cfg(test)]
919mod tests {
920    use crate::{derive_optionable, CodegenSettings};
921    use darling::FromMeta;
922    use proc_macro2::TokenStream;
923    use quote::quote;
924    use syn::{parse_quote, Path};
925
926    struct TestCase {
927        input: TokenStream,
928        output: TokenStream,
929    }
930
931    #[test]
932    #[allow(clippy::too_many_lines)]
933    fn test_optionable() {
934        let tcs = vec![
935            // named struct fields
936            TestCase {
937                input: quote! {
938                    #[derive(Optionable)]
939                    struct DeriveExample {
940                        name: String,
941                        pub surname: String,
942                    }
943                },
944                output: quote! {
945                    struct DeriveExampleOpt {
946                        name: Option<String>,
947                        pub surname: Option<String>
948                    }
949
950                    #[automatically_derived]
951                    impl ::optionable::Optionable for DeriveExample {
952                        type Optioned = DeriveExampleOpt;
953                    }
954
955                    #[automatically_derived]
956                    impl ::optionable::Optionable for DeriveExampleOpt {
957                        type Optioned = DeriveExampleOpt;
958                    }
959
960                    #[automatically_derived]
961                    impl ::optionable::OptionableConvert for DeriveExample {
962                        fn into_optioned (self) -> DeriveExampleOpt {
963                            DeriveExampleOpt  {
964                                name: Some(self.name),
965                                surname:Some(self.surname)
966                            }
967                        }
968
969                        fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::Error> {
970                            Ok(Self{
971                                name: value.name.ok_or(::optionable::Error { missing_field: "name" })?,
972                                surname: value.surname.ok_or(::optionable::Error { missing_field: "surname" })?
973                            })
974                        }
975
976                        fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::Error> {
977                            if let Some(other_value) = other.name {
978                                self.name = other_value;
979                            }
980                            if let Some(other_value) = other.surname {
981                                self.surname = other_value;
982                            }
983                            Ok(())
984                        }
985                    }
986
987                    #[automatically_derived]
988                    impl ::optionable::OptionedConvert<DeriveExample> for DeriveExampleOpt {
989                        fn from_optionable(value: DeriveExample) -> Self {
990                            ::optionable::OptionableConvert::into_optioned(value)
991                        }
992
993                        fn try_into_optionable(self) -> Result<DeriveExample, ::optionable::Error> {
994                            ::optionable::OptionableConvert::try_from_optioned(self)
995                        }
996
997                        fn merge_into(self, other: &mut DeriveExample) -> Result<(), ::optionable::Error> {
998                            ::optionable::OptionableConvert::merge(other, self)
999                        }
1000                    }
1001                },
1002            },
1003            // named struct fields, no convert impl
1004            TestCase {
1005                input: quote! {
1006                    #[derive(Optionable)]
1007                    #[optionable(no_convert)]
1008                    struct DeriveExample {
1009                        name: String,
1010                        pub surname: String,
1011                    }
1012                },
1013                output: quote! {
1014                    struct DeriveExampleOpt {
1015                        name: Option<String>,
1016                        pub surname: Option<String>
1017                    }
1018
1019                    #[automatically_derived]
1020                    impl ::optionable::Optionable for DeriveExample {
1021                        type Optioned = DeriveExampleOpt;
1022                    }
1023
1024                    #[automatically_derived]
1025                    impl ::optionable::Optionable for DeriveExampleOpt {
1026                        type Optioned = DeriveExampleOpt;
1027                    }
1028                },
1029            },
1030            // named struct fields with required fields
1031            TestCase {
1032                input: quote! {
1033                    #[derive(Optionable)]
1034                    struct DeriveExample {
1035                        name: String,
1036                        #[optionable(required)]
1037                        pub surname: String,
1038                        #[optionable(optioned_type=MyInt)]
1039                        pub id: i32,
1040                    }
1041                },
1042                output: quote! {
1043                    struct DeriveExampleOpt {
1044                        name: Option<String>,
1045                        pub surname: String,
1046                        pub id: Option<MyInt>
1047                    }
1048
1049                    #[automatically_derived]
1050                    impl ::optionable::Optionable for DeriveExample {
1051                        type Optioned = DeriveExampleOpt;
1052                    }
1053
1054                    #[automatically_derived]
1055                    impl ::optionable::Optionable for DeriveExampleOpt {
1056                        type Optioned = DeriveExampleOpt;
1057                    }
1058
1059                    #[automatically_derived]
1060                    impl ::optionable::OptionableConvert for DeriveExample {
1061                        fn into_optioned (self) -> DeriveExampleOpt {
1062                            DeriveExampleOpt  {
1063                                name: Some(self.name),
1064                                surname: self.surname,
1065                                id: Some (::optionable::OptionedConvert::from_optionable(self.id))
1066                            }
1067                        }
1068
1069                        fn try_from_optioned(value:DeriveExampleOpt ) -> Result <Self, ::optionable::Error> {
1070                            Ok (Self {
1071                                name: value.name.ok_or(::optionable::Error { missing_field: "name" })?,
1072                                surname: value.surname,
1073                                id: ::optionable::OptionedConvert::try_into_optionable(value.id.ok_or(::optionable::Error{ missing_field: "id" })?)?
1074                            })
1075                        }
1076
1077                        fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::Error> {
1078                            if let Some(other_value) = other.name {
1079                                self.name = other_value;
1080                            }
1081                            self.surname = other.surname;
1082                            if let Some (other_value) = other.id {
1083                                ::optionable::OptionedConvert::merge_into(other_value, &mut self.id)?;
1084                            }
1085                            Ok (())
1086                        }
1087                    }
1088
1089
1090                    #[automatically_derived]
1091                    impl ::optionable::OptionedConvert<DeriveExample> for DeriveExampleOpt {
1092                        fn from_optionable(value: DeriveExample) -> Self {
1093                            ::optionable::OptionableConvert::into_optioned(value)
1094                        }
1095
1096                        fn try_into_optionable(self) -> Result<DeriveExample, ::optionable::Error> {
1097                            ::optionable::OptionableConvert::try_from_optioned(self)
1098                        }
1099
1100                        fn merge_into(self, other: &mut DeriveExample) -> Result<(), ::optionable::Error> {
1101                            ::optionable::OptionableConvert::merge(other, self)
1102                        }
1103                    }
1104                },
1105            },
1106            // named struct fields with forwarded derives and Serialize annotations
1107            TestCase {
1108                input: quote! {
1109                    #[derive(Optionable)]
1110                    #[optionable(derive(Deserialize,Serialize,Default),suffix="Ac")]
1111                    #[optionable(attr_copy(attr=serde,key=rename))]
1112                    #[optionable_attr(serde(rename_all_fields = "camelCase", deny_unknown_fields))]
1113                    #[optionable_attr(serde(default))]
1114                    struct DeriveExample {
1115                        #[optionable_attr(serde(rename = "firstName"))]
1116                        name: String,
1117                        #[serde(rename="middle__name")]
1118                        middle_name: Option<String>,
1119                        surname: String,
1120                    }
1121                },
1122                output: quote! {
1123                    #[derive(Default, Deserialize, Serialize)]
1124                    #[serde(rename_all_fields = "camelCase", deny_unknown_fields)]
1125                    #[serde(default)]
1126                    struct DeriveExampleAc {
1127                        #[serde(rename = "firstName")]
1128                        #[serde(skip_serializing_if = "Option::is_none")]
1129                        name: Option<String>,
1130                        #[serde(rename = "middle__name")]
1131                        #[serde(skip_serializing_if = "Option::is_none")]
1132                        middle_name: <Option<String> as ::optionable::Optionable>::Optioned,
1133                        #[serde(skip_serializing_if = "Option::is_none")]
1134                        surname: Option<String>
1135                    }
1136
1137                    #[automatically_derived]
1138                    impl ::optionable::Optionable for DeriveExample {
1139                        type Optioned = DeriveExampleAc;
1140                    }
1141
1142                    #[automatically_derived]
1143                    impl ::optionable::Optionable for DeriveExampleAc {
1144                        type Optioned = DeriveExampleAc;
1145                    }
1146
1147                    #[automatically_derived]
1148                    impl ::optionable::OptionableConvert for DeriveExample {
1149                        fn into_optioned (self) -> DeriveExampleAc {
1150                            DeriveExampleAc  {
1151                                name: Some(self.name),
1152                                middle_name: ::optionable::OptionableConvert::into_optioned(self.middle_name),
1153                                surname: Some(self.surname)
1154                            }
1155                        }
1156
1157                        fn try_from_optioned(value: DeriveExampleAc ) -> Result <Self, ::optionable::Error> {
1158                            Ok(Self{
1159                                name: value.name.ok_or(::optionable::Error { missing_field: "name"})?,
1160                                middle_name: ::optionable::OptionableConvert::try_from_optioned(value.middle_name)?,
1161                                surname: value.surname.ok_or(::optionable::Error { missing_field: "surname"})?
1162                            })
1163                        }
1164
1165                        fn merge(&mut self, other: DeriveExampleAc ) -> Result<(), ::optionable::Error> {
1166                            if let Some(other_value) = other.name {
1167                                self.name =  other_value;
1168                            }
1169                            ::optionable::OptionableConvert::merge(&mut self.middle_name, other.middle_name)?;
1170                            if let Some(other_value) = other.surname {
1171                                self.surname =  other_value;
1172                            }
1173                            Ok(())
1174                        }
1175                    }
1176
1177
1178                    #[automatically_derived]
1179                    impl ::optionable::OptionedConvert<DeriveExample> for DeriveExampleAc {
1180                        fn from_optionable(value: DeriveExample) -> Self {
1181                            ::optionable::OptionableConvert::into_optioned(value)
1182                        }
1183
1184                        fn try_into_optionable(self) -> Result<DeriveExample, ::optionable::Error> {
1185                            ::optionable::OptionableConvert::try_from_optioned(self)
1186                        }
1187
1188                        fn merge_into(self, other: &mut DeriveExample) -> Result<(), ::optionable::Error> {
1189                            ::optionable::OptionableConvert::merge(other, self)
1190                        }
1191                    }
1192                },
1193            },
1194            // named struct fields with forwarded derives and Serialize annotations (full path variant)
1195            TestCase {
1196                input: quote! {
1197                    #[derive(Optionable)]
1198                    #[optionable(derive(serde::Deserialize,serde::Serialize),suffix="Ac")]
1199                    struct DeriveExample {
1200                        name: String,
1201                        surname: String,
1202                    }
1203                },
1204                output: quote! {
1205                    #[derive(serde::Deserialize, serde::Serialize)]
1206                    struct DeriveExampleAc {
1207                        #[serde(skip_serializing_if = "Option::is_none")]
1208                        name: Option<String>,
1209                        #[serde(skip_serializing_if = "Option::is_none")]
1210                        surname: Option<String>
1211                    }
1212
1213                    #[automatically_derived]
1214                    impl ::optionable::Optionable for DeriveExample {
1215                        type Optioned = DeriveExampleAc;
1216                    }
1217
1218                    #[automatically_derived]
1219                    impl ::optionable::Optionable for DeriveExampleAc {
1220                        type Optioned = DeriveExampleAc;
1221                    }
1222
1223                    #[automatically_derived]
1224                    impl ::optionable::OptionableConvert for DeriveExample {
1225                        fn into_optioned (self) -> DeriveExampleAc {
1226                            DeriveExampleAc {
1227                                name: Some(self.name),
1228                                surname: Some(self.surname)
1229                            }
1230                        }
1231
1232                        fn try_from_optioned(value: DeriveExampleAc ) -> Result <Self, ::optionable::Error> {
1233                             Ok(Self{
1234                                name: value.name.ok_or(::optionable::Error{ missing_field: "name"})?,
1235                                surname: value.surname.ok_or(::optionable::Error{ missing_field: "surname"})?
1236                            })
1237                        }
1238
1239                        fn merge(&mut self, other: DeriveExampleAc ) -> Result<(), ::optionable::Error> {
1240                            if let Some(other_value) = other.name {
1241                                self.name = other_value;
1242                            }
1243                            if let Some(other_value) = other.surname {
1244                                self.surname = other_value;
1245                            }
1246                            Ok(())
1247                        }
1248                    }
1249
1250                    #[automatically_derived]
1251                    impl ::optionable::OptionedConvert<DeriveExample> for DeriveExampleAc {
1252                        fn from_optionable(value: DeriveExample) -> Self {
1253                            ::optionable::OptionableConvert::into_optioned(value)
1254                        }
1255
1256                        fn try_into_optionable(self) -> Result<DeriveExample, ::optionable::Error> {
1257                            ::optionable::OptionableConvert::try_from_optioned(self)
1258                        }
1259
1260                        fn merge_into(self, other: &mut DeriveExample) -> Result<(), ::optionable::Error> {
1261                            ::optionable::OptionableConvert::merge(other, self)
1262                        }
1263                    }
1264                },
1265            },
1266            // unnamed struct fields
1267            TestCase {
1268                input: quote! {
1269                    #[derive(Optionable)]
1270                    struct DeriveExample(pub String, i32);
1271                },
1272                output: quote! {
1273                    struct DeriveExampleOpt(
1274                        pub Option<String>,
1275                        Option<i32>
1276                    );
1277
1278                    #[automatically_derived]
1279                    impl ::optionable::Optionable for DeriveExample {
1280                        type Optioned = DeriveExampleOpt;
1281                    }
1282
1283                    #[automatically_derived]
1284                    impl ::optionable::Optionable for DeriveExampleOpt {
1285                        type Optioned = DeriveExampleOpt;
1286                    }
1287
1288                    #[automatically_derived]
1289                    impl ::optionable::OptionableConvert for DeriveExample {
1290                        fn into_optioned (self) -> DeriveExampleOpt {
1291                            DeriveExampleOpt (
1292                                Some(self.0),
1293                                Some(self.1)
1294                            )
1295                        }
1296
1297                         fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::Error> {
1298                            Ok(Self(
1299                                value.0.ok_or(::optionable::Error { missing_field:"0" })?,
1300                                value.1.ok_or(::optionable::Error { missing_field: "1" })?
1301                            ))
1302                        }
1303
1304                        fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::Error> {
1305                            if let Some(other_value) = other.0 {
1306                                self.0 = other_value;
1307                            }
1308                            if let Some (other_value) = other.1 {
1309                                self.1 = other_value;
1310                            }
1311                            Ok (())
1312                        }
1313                    }
1314
1315
1316                    #[automatically_derived]
1317                    impl ::optionable::OptionedConvert<DeriveExample> for DeriveExampleOpt {
1318                        fn from_optionable(value: DeriveExample) -> Self {
1319                            ::optionable::OptionableConvert::into_optioned(value)
1320                        }
1321
1322                        fn try_into_optionable(self) -> Result<DeriveExample, ::optionable::Error> {
1323                            ::optionable::OptionableConvert::try_from_optioned(self)
1324                        }
1325
1326                        fn merge_into(self, other: &mut DeriveExample) -> Result<(), ::optionable::Error> {
1327                            ::optionable::OptionableConvert::merge(other, self)
1328                        }
1329                    }
1330                },
1331            },
1332            // unnamed struct fields with required
1333            TestCase {
1334                input: quote! {
1335                    #[derive(Optionable)]
1336                    struct DeriveExample(pub String, #[optionable(required)] i32);
1337                },
1338                output: quote! {
1339                    struct DeriveExampleOpt(
1340                        pub Option<String>,
1341                        i32
1342                    );
1343
1344                    #[automatically_derived]
1345                    impl ::optionable::Optionable for DeriveExample {
1346                        type Optioned = DeriveExampleOpt;
1347                    }
1348
1349                    #[automatically_derived]
1350                    impl ::optionable::Optionable for DeriveExampleOpt {
1351                        type Optioned = DeriveExampleOpt;
1352                    }
1353
1354                    # [automatically_derived]
1355                    impl ::optionable::OptionableConvert for DeriveExample {
1356                        fn into_optioned (self) -> DeriveExampleOpt {
1357                            DeriveExampleOpt (
1358                                Some(self.0),
1359                                self.1
1360                            )
1361                        }
1362
1363                         fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::Error> {
1364                            Ok(Self(
1365                                value.0.ok_or(::optionable::Error { missing_field: "0" })?,
1366                                value.1))
1367                        }
1368
1369                        fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::Error> {
1370                            if let Some(other_value) = other.0 {
1371                                self.0 = other_value;
1372                            }
1373                            self.1 = other.1;
1374                            Ok (())
1375                        }
1376                    }
1377
1378
1379                    #[automatically_derived]
1380                    impl ::optionable::OptionedConvert<DeriveExample> for DeriveExampleOpt {
1381                        fn from_optionable(value: DeriveExample) -> Self {
1382                            ::optionable::OptionableConvert::into_optioned(value)
1383                        }
1384
1385                        fn try_into_optionable(self) -> Result<DeriveExample, ::optionable::Error> {
1386                            ::optionable::OptionableConvert::try_from_optioned(self)
1387                        }
1388
1389                        fn merge_into(self, other: &mut DeriveExample) -> Result<(), ::optionable::Error> {
1390                            ::optionable::OptionableConvert::merge(other, self)
1391                        }
1392                    }
1393                },
1394            },
1395            // named struct fields with generics
1396            TestCase {
1397                input: quote! {
1398                    #[derive(Optionable)]
1399                    #[optionable(derive(Serialize, Deserialize))]
1400                    struct DeriveExample<'a, T, T2: Serialize, T3> {
1401                        output: T,
1402                        input: Cow<'a, T2>,
1403                        #[optionable(required)]
1404                        extra: T3,
1405                    }
1406                },
1407                output: quote! {
1408                    #[derive (Deserialize, Serialize)]
1409                    struct DeriveExampleOpt<'a, T, T2: Serialize, T3>
1410                        where T: ::optionable::Optionable,
1411                              <T as ::optionable::Optionable>::Optioned: Sized + serde::de::DeserializeOwned + Serialize,
1412                              Cow<'a ,T2>: ::optionable::Optionable,
1413                              <Cow<'a,T2> as ::optionable::Optionable>::Optioned: Sized + serde::de::DeserializeOwned + Serialize
1414                    {
1415                        #[serde(skip_serializing_if="Option::is_none")]
1416                        output: Option< <T as ::optionable::Optionable>::Optioned>,
1417                        #[serde(skip_serializing_if="Option::is_none")]
1418                        input: Option< <Cow<'a, T2> as ::optionable::Optionable>::Optioned>,
1419                        extra: T3
1420                    }
1421
1422                    #[automatically_derived]
1423                    impl<'a, T, T2: Serialize, T3> ::optionable::Optionable for DeriveExample<'a, T, T2, T3>
1424                        where T: ::optionable::Optionable,
1425                              <T as ::optionable::Optionable>::Optioned: Sized + serde::de::DeserializeOwned + Serialize,
1426                              Cow<'a, T2>: ::optionable::Optionable,
1427                              <Cow<'a, T2> as ::optionable::Optionable>::Optioned: Sized + serde::de::DeserializeOwned + Serialize
1428                    {
1429                        type Optioned = DeriveExampleOpt<'a, T,T2,T3>;
1430                    }
1431
1432                    #[automatically_derived]
1433                    impl<'a, T, T2: Serialize, T3> ::optionable::Optionable for DeriveExampleOpt<'a ,T, T2, T3>
1434                        where T: ::optionable::Optionable,
1435                              <T as ::optionable::Optionable>::Optioned: Sized + serde::de::DeserializeOwned + Serialize,
1436                              Cow<'a, T2>: ::optionable::Optionable,
1437                              <Cow<'a, T2> as ::optionable::Optionable>::Optioned: Sized + serde::de::DeserializeOwned + Serialize
1438                    {
1439                        type Optioned = DeriveExampleOpt<'a, T, T2, T3>;
1440                    }
1441
1442                    #[automatically_derived]
1443                    impl <'a, T, T2:Serialize, T3> ::optionable::OptionableConvert for DeriveExample<'a, T, T2, T3>
1444                        where T: ::optionable::OptionableConvert,
1445                              <T as ::optionable::Optionable>::Optioned: Sized + serde::de::DeserializeOwned + Serialize,
1446                              Cow<'a, T2>: ::optionable::OptionableConvert,
1447                              <Cow<'a, T2> as ::optionable::Optionable>::Optioned: Sized + serde::de::DeserializeOwned + Serialize
1448                    {
1449                        fn into_optioned (self) -> DeriveExampleOpt<'a, T, T2, T3> {
1450                            DeriveExampleOpt::<'a, T, T2, T3> {
1451                                output: Some(::optionable::OptionableConvert::into_optioned(self.output)),
1452                                input: Some(::optionable::OptionableConvert::into_optioned(self.input)),
1453                                extra: self.extra
1454                            }
1455                        }
1456
1457                         fn try_from_optioned(value: DeriveExampleOpt<'a, T, T2, T3> ) -> Result <Self, ::optionable::Error> {
1458                             Ok(Self{
1459                                output: ::optionable::OptionableConvert::try_from_optioned(value.output.ok_or(::optionable::Error { missing_field: "output" })?)?,
1460                                input: ::optionable::OptionableConvert::try_from_optioned(value.input.ok_or(::optionable::Error { missing_field: "input" })?)?,
1461                                extra: value.extra
1462                            })
1463                        }
1464
1465                        fn merge(&mut self, other: DeriveExampleOpt<'a, T, T2, T3> ) -> Result<(), ::optionable::Error> {
1466                            if let Some(other_value) = other.output {
1467                                ::optionable::OptionableConvert::merge(&mut self.output, other_value)?;
1468                            }
1469                            if let Some(other_value) = other.input {
1470                                ::optionable::OptionableConvert::merge(&mut self.input, other_value)?;
1471                            }
1472                            self.extra=other.extra;
1473                            Ok(())
1474                        }
1475                    }
1476
1477
1478                    #[automatically_derived]
1479                    impl <'a, T, T2:Serialize, T3> ::optionable::OptionedConvert<DeriveExample<'a, T, T2, T3> > for DeriveExampleOpt<'a, T, T2, T3>
1480                        where T: ::optionable::OptionableConvert,
1481                              <T as ::optionable::Optionable>::Optioned: Sized + serde::de::DeserializeOwned + Serialize,
1482                              Cow<'a, T2>: ::optionable::OptionableConvert,
1483                              <Cow<'a, T2> as ::optionable::Optionable>::Optioned: Sized + serde::de::DeserializeOwned + Serialize
1484                    {
1485                        fn from_optionable(value: DeriveExample<'a, T, T2, T3>) -> Self {
1486                            ::optionable::OptionableConvert::into_optioned(value)
1487                        }
1488
1489                        fn try_into_optionable(self) -> Result<DeriveExample<'a, T, T2, T3>, ::optionable::Error> {
1490                            ::optionable::OptionableConvert::try_from_optioned(self)
1491                        }
1492
1493                        fn merge_into(self, other: &mut DeriveExample<'a, T, T2, T3>) -> Result<(), ::optionable::Error> {
1494                            ::optionable::OptionableConvert::merge(other, self)
1495                        }
1496                    }
1497                },
1498            },
1499            TestCase {
1500                input: quote! {
1501                    #[derive(Optionable)]
1502                    enum DeriveExample {
1503                        Unit,
1504                        Plain(String),
1505                        Address{street: String, number: u32},
1506                        Address2(String,u32),
1507                    }
1508                },
1509                output: quote! {
1510                    enum DeriveExampleOpt {
1511                        Unit,
1512                        Plain( Option<String> ),
1513                        Address{ street: Option<String>, number:Option<u32> },
1514                        Address2( Option<String>, Option<u32> )
1515                    }
1516
1517                    #[automatically_derived]
1518                    impl ::optionable::Optionable for DeriveExample {
1519                        type Optioned = DeriveExampleOpt;
1520                    }
1521
1522                    #[automatically_derived]
1523                    impl ::optionable::Optionable for DeriveExampleOpt {
1524                        type Optioned = DeriveExampleOpt;
1525                    }
1526
1527                    #[automatically_derived]
1528                    impl ::optionable::OptionableConvert for DeriveExample {
1529                        fn into_optioned (self) -> DeriveExampleOpt {
1530                            match self{
1531                                Self::Unit => DeriveExampleOpt::Unit,
1532                                Self::Plain(self_0) => DeriveExampleOpt::Plain(
1533                                    Some(self_0)
1534                                ),
1535                                Self::Address{street: self_street, number: self_number} => DeriveExampleOpt::Address{
1536                                    street: Some(self_street),
1537                                    number: Some(self_number)
1538                                },
1539                                Self::Address2(self_0, self_1) => DeriveExampleOpt::Address2(
1540                                    Some(self_0),
1541                                    Some(self_1)
1542                                )
1543                            }
1544                        }
1545
1546                         fn try_from_optioned(other: DeriveExampleOpt) -> Result <Self, ::optionable::Error> {
1547                            Ok (match other {
1548                                DeriveExampleOpt::Unit => Self::Unit,
1549                                DeriveExampleOpt::Plain(other_0) => Self::Plain(
1550                                    other_0.ok_or(::optionable::Error { missing_field: "0" })?
1551                                ),
1552                                DeriveExampleOpt::Address{street: other_street, number: other_number} => Self::Address{
1553                                    street: other_street.ok_or(::optionable::Error { missing_field: "street" })?,
1554                                    number: other_number.ok_or(::optionable::Error { missing_field: "number" })?
1555                                },
1556                                DeriveExampleOpt::Address2(other_0, other_1) => Self::Address2(
1557                                    other_0.ok_or(::optionable::Error { missing_field: "0"})?,
1558                                    other_1.ok_or(::optionable::Error { missing_field: "1"})?)
1559                            })
1560                        }
1561
1562                        fn merge(&mut self, other: DeriveExampleOpt) -> Result<(), ::optionable::Error> {
1563                            match other {
1564                                DeriveExampleOpt::Unit => {
1565                                    if let Self::Unit = self {} else {
1566                                        *self = Self::try_from_optioned(DeriveExampleOpt::Unit)?;
1567                                    }
1568                                },
1569                                DeriveExampleOpt::Plain(other_0) => {
1570                                    if let Self::Plain(self_0) = self{
1571                                        if let Some(other_value) = other_0 {
1572                                            *self_0 = other_value;
1573                                        }
1574                                    } else {
1575                                        *self = Self::try_from_optioned(DeriveExampleOpt::Plain(other_0))?;
1576                                    }
1577                                },
1578                                DeriveExampleOpt::Address{street: other_street, number: other_number} => {
1579                                    if let Self::Address{street: self_street, number: self_number}  = self{
1580                                        if let Some(other_value) = other_street {
1581                                            *self_street = other_value;
1582                                        }
1583                                        if let Some(other_value) = other_number {
1584                                            *self_number = other_value;
1585                                        }
1586                                    } else {
1587                                        *self = Self::try_from_optioned(DeriveExampleOpt::Address{street: other_street, number: other_number})?;
1588                                    }
1589                                },
1590                                DeriveExampleOpt::Address2(other_0, other_1) => {
1591                                    if let Self::Address2(self_0, self_1) = self{
1592                                        if let Some(other_value) = other_0 {
1593                                            *self_0 = other_value;
1594                                        }
1595                                        if let Some(other_value) = other_1 {
1596                                            *self_1 = other_value;
1597                                        }
1598                                    } else {
1599                                        *self = Self::try_from_optioned(DeriveExampleOpt::Address2(other_0, other_1))?;
1600                                    }
1601                                }
1602                            }
1603                            Ok(())
1604                        }
1605                    }
1606
1607                    #[automatically_derived]
1608                    impl ::optionable::OptionedConvert<DeriveExample> for DeriveExampleOpt {
1609                        fn from_optionable(value: DeriveExample) -> Self {
1610                            ::optionable::OptionableConvert::into_optioned(value)
1611                        }
1612
1613                        fn try_into_optionable(self) -> Result<DeriveExample, ::optionable::Error> {
1614                            ::optionable::OptionableConvert::try_from_optioned(self)
1615                        }
1616
1617                        fn merge_into(self, other: &mut DeriveExample) -> Result<(), ::optionable::Error> {
1618                            ::optionable::OptionableConvert::merge(other, self)
1619                        }
1620                    }
1621                },
1622            },
1623            TestCase {
1624                input: quote! {
1625                    #[derive(Optionable)]
1626                    enum DeriveExample {
1627                        Unit,
1628                        Unit2,
1629                        Unit3
1630                    }
1631                },
1632                output: quote! {
1633                    #[automatically_derived]
1634                    impl ::optionable::Optionable for DeriveExample {
1635                        type Optioned = Self;
1636                    }
1637
1638                    #[automatically_derived]
1639                    impl ::optionable::OptionableConvert for DeriveExample {
1640                        fn into_optioned(self) -> DeriveExample{
1641                            self
1642                        }
1643
1644                        fn try_from_optioned(value: Self::Optioned) -> Result <Self , ::optionable::Error> {
1645                            Ok (value)
1646                        }
1647
1648                        fn merge (&mut self , other: Self::Optioned) -> Result <() , ::optionable::Error> {
1649                            *self = other;
1650                            Ok(())
1651                        }
1652                    }
1653                },
1654            },
1655        ];
1656        for tc in tcs {
1657            let input = syn::parse2(tc.input).unwrap();
1658            let output = derive_optionable(input, None).unwrap();
1659            println!("{output}");
1660            assert_eq!(tc.output.to_string(), output.to_string());
1661        }
1662    }
1663
1664    #[test]
1665    #[allow(clippy::too_many_lines)]
1666    /// Tests of the crate replacement settings available via codegen settings only
1667    fn test_crate_replacement() {
1668        let tcs = vec![TestCase {
1669            input: quote! {
1670                #[derive(Optionable)]
1671                struct DeriveExample {
1672                    name: crate::Name,
1673                    pub surname: Box<crate::SurName>,
1674                }
1675            },
1676            output: quote! {
1677                struct DeriveExampleOpt {
1678                    name: Option< <::testcrate::Name as crate::Optionable>::Optioned>,
1679                    pub surname: Option< <Box<::testcrate::SurName> as crate::Optionable>::Optioned>
1680                }
1681
1682                #[automatically_derived]
1683                impl crate::Optionable for crate_prefix::DeriveExample {
1684                    type Optioned = DeriveExampleOpt;
1685                }
1686
1687                #[automatically_derived]
1688                impl crate::Optionable for DeriveExampleOpt {
1689                    type Optioned = DeriveExampleOpt;
1690                }
1691
1692                #[automatically_derived]
1693                impl crate::OptionableConvert for crate_prefix::DeriveExample {
1694                    fn into_optioned (self) -> DeriveExampleOpt {
1695                        DeriveExampleOpt  {
1696                            name: Some(crate::OptionableConvert::into_optioned(self.name)),
1697                            surname: Some(crate::OptionableConvert::into_optioned(self.surname))
1698                        }
1699                    }
1700
1701                    fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, crate::Error> {
1702                        Ok(Self{
1703                            name: crate::OptionableConvert::try_from_optioned(value.name.ok_or(crate::Error { missing_field: "name" })?)?,
1704                            surname: crate::OptionableConvert::try_from_optioned(value.surname.ok_or(crate::Error { missing_field: "surname" })?)?
1705                        })
1706                    }
1707
1708                    fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), crate::Error> {
1709                        if let Some(other_value) = other.name {
1710                             crate::OptionableConvert::merge(&mut self.name, other_value)?;
1711                        }
1712                        if let Some(other_value) = other.surname {
1713                             crate::OptionableConvert::merge(&mut self.surname, other_value)?;
1714                        }
1715                        Ok(())
1716                    }
1717                }
1718
1719                #[automatically_derived]
1720                impl crate::OptionedConvert<crate_prefix::DeriveExample> for DeriveExampleOpt {
1721                    fn from_optionable(value: crate_prefix::DeriveExample) -> Self {
1722                        crate::OptionableConvert::into_optioned(value)
1723                    }
1724
1725                    fn try_into_optionable(self) -> Result<crate_prefix::DeriveExample, crate::Error> {
1726                        crate::OptionableConvert::try_from_optioned(self)
1727                    }
1728
1729                    fn merge_into(self, other: &mut crate_prefix::DeriveExample) -> Result<(), crate::Error> {
1730                       crate::OptionableConvert::merge(other, self)
1731                    }
1732                }
1733            },
1734        }];
1735        for tc in tcs {
1736            let input = syn::parse2(tc.input).unwrap();
1737            let output = derive_optionable(
1738                input,
1739                Some(&CodegenSettings {
1740                    ty_prefix: Some(Path::from_string("crate_prefix").unwrap()),
1741                    optionable_crate_name: Path::from_string("crate").unwrap(),
1742                    input_crate_replacement: Some(parse_quote!(testcrate)),
1743                }),
1744            )
1745            .unwrap();
1746            assert_eq!(tc.output.to_string(), output.to_string());
1747        }
1748    }
1749}