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