1mod 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))]
50pub(crate) struct TypeHelperAttributes {
52 #[darling(multiple)]
56 attr_copy: Vec<FieldAttributeToCopy>,
57 #[darling(multiple)]
59 derive: Vec<PathList>,
60 suffix: Option<LitStr>,
62 no_convert: Option<()>,
64 k8s_openapi: Option<TypeHelperAttributesK8sOpenapi>,
68 kube: Option<TypeHelperAttributesKube>,
72}
73
74#[derive(FromMeta)]
75pub(crate) struct TypeHelperAttributesK8sOpenapi {
79 metadata: Option<()>,
83 resource: Option<()>,
87}
88
89#[derive(FromMeta)]
90pub(crate) struct TypeHelperAttributesKube {
93 resource: Option<()>,
98}
99
100#[derive(FromMeta)]
101pub struct FieldAttributeToCopy {
105 pub attr: Path,
106 pub key: Option<Path>,
107}
108
109#[derive(FromDeriveInput, Debug, Clone)]
110#[darling(attributes(optionable))]
111pub struct CodegenSettings {
115 pub optionable_crate_name: Path,
119 pub ty_prefix: Option<Path>,
122 pub input_crate_replacement: Option<Ident>,
126}
127
128fn 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 !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))]
177struct FieldHelperAttributes {
179 required: Option<()>,
181 #[darling(rename = "optioned_type")]
184 optioned_ty: Option<Path>,
185}
186
187#[must_use]
189pub fn attribute_no_convert() -> Attribute {
190 parse_quote!(#[optionable(no_convert)])
191}
192
193#[must_use]
195pub fn attribute_suffix(suffix: &str) -> Attribute {
196 parse_quote!(#[optionable(suffix=#suffix)])
197}
198
199#[must_use]
202pub fn attribute_derives(derives: &PathList) -> Attribute {
203 parse_quote!(#[optionable(derive(#(#derives),*))])
204}
205
206#[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 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() || derive.iter().any(|el|is_serialize(el.as_str())))
295 .then(|| quote!(#[serde(skip_serializing_if = "Option::is_none")]));
296
297 struct Derived {
299 enum_struct: TokenStream,
301 fields: TokenStream,
303 where_clause_struct_enum: WhereClause,
305 where_clause_impl_optionable: WhereClause,
307 where_clause_impl_optionable_convert: Option<WhereClause>,
309 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
592fn 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
623fn 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
650fn 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
677fn 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
726fn 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}
786fn 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 "i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64", "u128", "usize", "f32",
801 "f64", "char", "bool", "String", "OsString",
803];
804
805fn 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
818fn 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 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 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 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 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 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 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 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 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 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 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 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}