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