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::{HashMap, HashSet};
34use std::default::Default;
35use syn::parse::Parser;
36use syn::spanned::Spanned;
37use syn::{
38 parse_quote, Attribute, Data, DeriveInput, Error, Field, LitStr, Meta, MetaList, Path, Type,
39 TypePath, WhereClause,
40};
41
42const HELPER_IDENT: &str = "optionable";
43const HELPER_ATTR_IDENT: &str = "optionable_attr";
44const ERR_MSG_HELPER_ATTR_ENUM_VARIANTS: &str =
45 "#[optionable] helper attributes not supported on enum variant level.";
46
47#[derive(FromDeriveInput)]
48#[darling(attributes(optionable))]
49pub(crate) struct TypeHelperAttributes {
51 #[darling(multiple)]
53 derive: Vec<PathList>,
54 suffix: Option<LitStr>,
56 no_convert: Option<()>,
58 k8s_openapi: Option<TypeHelperAttributesK8sOpenapi>,
62 kube: Option<TypeHelperAttributesKube>,
66 #[darling(multiple)]
70 attr_copy: Vec<FieldAttributeToCopy>,
71}
72
73#[derive(FromMeta)]
74pub(crate) struct TypeHelperAttributesK8sOpenapi {
78 metadata: Option<()>,
82 resource: Option<()>,
86}
87
88#[derive(FromMeta)]
89pub(crate) struct TypeHelperAttributesKube {
92 resource: Option<()>,
97}
98
99#[derive(FromMeta)]
100pub struct FieldAttributeToCopy {
104 pub attr: Path,
105 pub key: Option<Path>,
106}
107
108#[derive(FromDeriveInput, Debug, Clone)]
109#[darling(attributes(optionable))]
110pub struct CodegenSettings {
114 pub optionable_crate_name: Path,
118 pub ty_prefix: Option<Path>,
121 pub input_crate_replacement: Option<Ident>,
125}
126
127fn field_attr_copy_hashmap(
130 input: Vec<FieldAttributeToCopy>,
131 attr_kube: Option<&TypeHelperAttributesKube>,
132) -> HashMap<Path, HashSet<Path>> {
133 let mut result = HashMap::<Path, HashSet<Path>>::new();
134 for el in input {
135 if let Some(key) = el.key {
136 if let Some(entry) = result.get_mut(&el.attr) {
137 entry.insert(key);
138 } else {
139 result.insert(el.attr, HashSet::from([key]));
140 }
141 }
142 }
143 if attr_kube.is_some() {
144 let path_serde = Path::from_string("serde").unwrap();
145 let path_rename = Path::from_string("rename").unwrap();
146 if let Some(entry) = result.get_mut(&path_serde) {
147 if !entry.is_empty() {
149 entry.insert(path_rename);
150 }
151 } else {
152 result.insert(path_serde, HashSet::from([path_rename]));
153 }
154 }
155 result
156}
157
158impl Default for CodegenSettings {
159 fn default() -> Self {
160 Self {
161 optionable_crate_name: parse_quote!(::optionable),
162 ty_prefix: None,
163 input_crate_replacement: None,
164 }
165 }
166}
167
168#[derive(FromAttributes)]
169#[darling(attributes(optionable))]
170struct FieldHelperAttributes {
172 required: Option<()>,
174}
175
176#[must_use]
178pub fn attribute_no_convert() -> Attribute {
179 parse_quote!(#[optionable(no_convert)])
180}
181
182#[must_use]
184pub fn attribute_suffix(suffix: &str) -> Attribute {
185 parse_quote!(#[optionable(suffix=#suffix)])
186}
187
188#[must_use]
191pub fn attribute_derives(derives: &PathList) -> Attribute {
192 parse_quote!(#[optionable(derive(#(#derives),*))])
193}
194
195#[allow(clippy::too_many_lines)]
201#[allow(clippy::items_after_statements)]
202pub fn derive_optionable(
203 input: DeriveInput,
204 settings: Option<&CodegenSettings>,
205) -> syn::Result<TokenStream> {
206 let attrs = TypeHelperAttributes::from_derive_input(&input)?;
207 error_missing_features(&attrs)?;
208 let k8s_resource_type = k8s_resource_type(&attrs)?;
209 let attr_copy_identifier = field_attr_copy_hashmap(attrs.attr_copy, attrs.kube.as_ref());
210
211 let mut derive = attrs
212 .derive
213 .into_iter()
214 .flat_map(|el| el.iter().cloned().collect::<Vec<_>>())
215 .collect::<Vec<_>>();
216 if (attrs.k8s_openapi.is_some() || attrs.kube.is_some())
217 && let Some(k8s_derives) = &mut k8s_derives(&input)
218 {
219 derive.append(k8s_derives);
220 }
221 let settings = settings.map(Cow::Borrowed).unwrap_or_default();
222 let crate_name = &settings.optionable_crate_name;
223 let ty_attr_forwarded = forwarded_attributes(&input.attrs, &attr_copy_identifier)?;
224 let k8s_openapi_attrs =
225 (attrs.k8s_openapi.is_some() || attrs.kube.is_some()).then(|| k8s_type_attr(&input));
226 let vis = input.vis;
227 let ty_ident_opt = {
228 let suffix = attrs.suffix.map_or_else(
229 || {
230 if attrs.kube.is_some() || attrs.k8s_openapi.is_some() {
231 Cow::Borrowed("Ac")
232 } else {
233 Cow::Borrowed("Opt")
234 }
235 },
236 |s| Cow::Owned(s.value()),
237 );
238 Ident::new(&(input.ident.to_string() + &suffix), input.ident.span())
239 };
240 let ty_ident = if let Some(mut ty_prefix) = settings.ty_prefix.clone() {
241 ty_prefix.segments.push(input.ident.into());
242 ty_prefix
243 } else {
244 input.ident.into()
245 };
246 let (impl_generics, ty_generics, _) = input.generics.split_for_impl();
247 let generics_colon = (!input.generics.params.is_empty()).then(|| quote! {::});
248
249 let skip_optionable_if_serde_serialize = (attrs.k8s_openapi.is_some() || derive.iter().any(is_serialize))
251 .then(|| quote!(#[serde(skip_serializing_if = "Option::is_none")]));
252
253 struct Derived {
255 enum_struct: TokenStream,
257 fields: TokenStream,
259 where_clause_struct_enum: WhereClause,
261 where_clause_impl_optionable: WhereClause,
263 impl_optionable_convert: Option<TokenStream>,
265 }
266 let Derived {
267 enum_struct,
268 fields,
269 where_clause_struct_enum,
270 where_clause_impl_optionable,
271 impl_optionable_convert,
272 } = match input.data {
273 Data::Struct(s) => {
274 let mut struct_parsed = into_field_handling(
275 crate_name.to_owned(),
276 s.fields,
277 settings.input_crate_replacement.as_ref(),
278 )?;
279 k8s_adjust_fields(
280 &mut struct_parsed,
281 attrs.k8s_openapi.as_ref(),
282 attrs.kube.as_ref(),
283 k8s_resource_type.as_ref(),
284 crate_name,
285 &ty_ident_opt,
286 &ty_generics,
287 )?;
288 let WhereClauses {
289 struct_enum_def: where_clause_struct_enum,
290 impl_optionable: where_clause_impl_optionable,
291 impl_optionable_convert: where_clause_impl_optionable_convert,
292 } = where_clauses(
293 crate_name,
294 settings.input_crate_replacement.as_ref(),
295 &input.generics,
296 &derive,
297 attrs.no_convert.is_some(),
298 &struct_parsed.fields,
299 );
300 let unnamed_struct_semicolon =
301 (struct_parsed.struct_type == StructType::Unnamed).then(|| quote!(;));
302 let optioned_fields = optioned_fields(
303 &struct_parsed,
304 skip_optionable_if_serde_serialize.as_ref(),
305 &attr_copy_identifier,
306 )?;
307
308 let impl_optionable_convert = attrs.no_convert.is_none().then(|| {
309 let into_optioned_fields = into_optioned(&struct_parsed, |selector| quote! { self.#selector });
310 let try_from_optioned_fields =
311 try_from_optioned(&struct_parsed, |selector| quote! { value.#selector });
312 let merge_fields = merge_fields(
313 &struct_parsed,
314 |selector| quote! { self.#selector },
315 |selector| quote! { other.#selector },
316 true);
317 quote! {
318 #[automatically_derived]
319 impl #impl_generics #crate_name::OptionableConvert for #ty_ident #ty_generics #where_clause_impl_optionable_convert{
320 fn into_optioned(self) -> #ty_ident_opt #ty_generics {
321 #ty_ident_opt #generics_colon #ty_generics #into_optioned_fields
322 }
323
324 fn try_from_optioned(value: #ty_ident_opt #ty_generics) -> Result<Self, #crate_name::Error>{
325 Ok(Self #try_from_optioned_fields)
326 }
327
328 fn merge(&mut self, other: #ty_ident_opt #ty_generics) -> Result<(), #crate_name::Error>{
329 #merge_fields
330 Ok(())
331 }
332 }
333 }
334 });
335 Derived {
336 enum_struct: quote! {struct},
337 fields: quote! {#optioned_fields #unnamed_struct_semicolon},
338 where_clause_struct_enum,
339 where_clause_impl_optionable,
340 impl_optionable_convert,
341 }
342 }
343 Data::Enum(e) => {
344 let self_prefix = quote! {self_};
345 let other_prefix = quote! {other_};
346
347 let variants = e
348 .variants
349 .into_iter()
350 .map(|v| {
351 error_on_helper_attributes(&v.attrs, ERR_MSG_HELPER_ATTR_ENUM_VARIANTS)?;
352 let mut field_handling = into_field_handling(
353 crate_name.to_owned(),
354 v.fields,
355 settings.input_crate_replacement.as_ref(),
356 )?;
357 k8s_adjust_fields(
358 &mut field_handling,
359 attrs.k8s_openapi.as_ref(),
360 attrs.kube.as_ref(),
361 k8s_resource_type.as_ref(),
362 crate_name,
363 &ty_ident_opt,
364 &ty_generics,
365 )?;
366 Ok::<_, Error>((
367 v.ident,
368 forwarded_attributes(&v.attrs, &attr_copy_identifier)?,
369 field_handling,
370 ))
371 })
372 .collect::<Result<Vec<_>, _>>()?;
373 let all_fields = variants
374 .iter()
375 .flat_map(|(_, _, fields)| &fields.fields)
376 .collect::<Vec<_>>();
377 let WhereClauses {
378 struct_enum_def: where_clause_struct_enum,
379 impl_optionable: where_clause_impl_optionable,
380 impl_optionable_convert: where_clause_impl_optionable_convert,
381 } = where_clauses(
382 crate_name,
383 settings.input_crate_replacement.as_ref(),
384 &input.generics,
385 &derive,
386 attrs.no_convert.is_some(),
387 all_fields,
388 );
389
390 let optioned_variants = variants
391 .iter()
392 .map(|(variant, forward_attrs, f)| {
393 let fields = optioned_fields(
394 f,
395 skip_optionable_if_serde_serialize.as_ref(),
396 &attr_copy_identifier,
397 )?;
398 Ok::<_, Error>(quote!( #forward_attrs #variant #fields ))
399 })
400 .collect::<Result<Vec<_>, _>>()?;
401
402 let impl_optionable_convert = attrs.no_convert.is_none().then(|| {
403 let (into_variants, try_from_variants, merge_variants): (Vec<_>, Vec<_>, Vec<_>) = variants
404 .iter()
405 .map(|(variant, _, struct_parsed)| {
406 let fields_into = into_optioned(struct_parsed, |selector| {
407 format_ident!("{self_prefix}{selector}").to_token_stream()
408 });
409 let fields_try_from = try_from_optioned
410 (struct_parsed, |selector| {
411 format_ident!("{other_prefix}{selector}").to_token_stream()
412 });
413 let fields_merge = merge_fields(struct_parsed,
414 |selector| format_ident!("{self_prefix}{selector}").to_token_stream(),
415 |selector| format_ident!("{other_prefix}{selector}").to_token_stream(),
416 false);
417 let self_destructure = destructure(struct_parsed, &self_prefix)?;
418 let other_destructure = destructure(struct_parsed, &other_prefix)?;
419 Ok::<_, Error>((
420 quote!( Self::#variant #self_destructure => #ty_ident_opt::#variant #fields_into ),
421 quote!( #ty_ident_opt::#variant #other_destructure => Self::#variant #fields_try_from ),
422 quote!( #ty_ident_opt::#variant #other_destructure => {
423 if let Self::#variant #self_destructure = self {
424 #fields_merge
425 } else {
426 *self = Self::try_from_optioned(#ty_ident_opt::#variant #other_destructure)?;
427 }
428 })
429 ))
430 })
431 .collect::<Result<Vec<_>, _>>()?
432 .into_iter().multiunzip();
433 Ok::<_, Error>(quote! {
434 #[automatically_derived]
435 impl #impl_generics #crate_name::OptionableConvert for #ty_ident #ty_generics # where_clause_impl_optionable_convert {
436 fn into_optioned(self) -> #ty_ident_opt #ty_generics {
437 match self {
438 #(#into_variants),*
439 }
440 }
441
442 fn try_from_optioned(other: #ty_ident_opt #ty_generics)->Result<Self,#crate_name::Error>{
443 Ok(match other{
444 #(#try_from_variants),*
445 })
446 }
447
448 fn merge(&mut self, other: #ty_ident_opt #ty_generics) -> Result<(), #crate_name::Error>{
449 match other{
450 #(#merge_variants),*
451 }
452 Ok(())
453 }
454 }
455 })
456 }).transpose()?;
457 Derived {
458 enum_struct: quote! {enum},
459 fields: quote! {{#(#optioned_variants),*}},
460 where_clause_struct_enum,
461 where_clause_impl_optionable,
462 impl_optionable_convert,
463 }
464 }
465 Data::Union(_) => return error("#[derive(Optionable)] not supported for unit structs"),
466 };
467
468 let derives = (!derive.is_empty()).then(|| quote! {#[derive(#(#derive),*)]});
469
470 let k8s_openapi_impl_resource = attrs
471 .k8s_openapi
472 .as_ref()
473 .is_some_and(|attr| attr.resource.is_some())
474 .then(|| {
475 k8s_openapi_impl_resource(
476 &ty_ident,
477 &ty_ident_opt,
478 &impl_generics,
479 &ty_generics,
480 &where_clause_impl_optionable,
481 )
482 });
483 let impl_k8s_metadata = attrs
484 .k8s_openapi
485 .as_ref()
486 .is_some_and(|attr| attr.metadata.is_some())
487 .then(|| {
488 k8s_openapi_impl_metadata(
489 &ty_ident,
490 &ty_ident_opt,
491 &impl_generics,
492 &ty_generics,
493 &where_clause_impl_optionable,
494 )
495 });
496 let kube_derive_resource = attrs
497 .kube
498 .is_some_and(|attr| attr.resource.is_some())
499 .then(|| {
500 quote! {
501 #[derive(kube::Resource)]
502 #[resource(inherit = #ty_ident )]
503 }
504 });
505 Ok(quote! {
506 #derives
507 #kube_derive_resource
508 #ty_attr_forwarded
509 #k8s_openapi_attrs
510 #vis #enum_struct #ty_ident_opt #impl_generics #where_clause_struct_enum #fields
511
512 #[automatically_derived]
513 impl #impl_generics #crate_name::Optionable for #ty_ident #ty_generics #where_clause_impl_optionable {
514 type Optioned = #ty_ident_opt #ty_generics;
515 }
516
517 #[automatically_derived]
518 impl #impl_generics #crate_name::Optionable for #ty_ident_opt #ty_generics #where_clause_impl_optionable {
519 type Optioned = #ty_ident_opt #ty_generics;
520 }
521
522 #impl_optionable_convert
523
524 #k8s_openapi_impl_resource
525 #impl_k8s_metadata
526 })
527}
528
529fn optioned_fields(
533 fields: &StructParsed,
534 serde_attributes: Option<&TokenStream>,
535 field_attr_forwards: &HashMap<Path, HashSet<Path>>,
536) -> Result<TokenStream, Error> {
537 let fields_token = fields.fields.iter().map(
538 |FieldParsed {
539 field: Field { attrs, vis, ident, ty, .. },
540 handling,
541 }| {
542 let forwarded_attrs = forwarded_attributes(attrs, field_attr_forwards)?;
543 let optioned_ty = optioned_ty(&fields.crate_name, ty);
544 let colon = ident.as_ref().map(|_| quote! {:});
545 Ok::<_, Error>(match handling {
546 FieldHandling::Required | FieldHandling::OptionedOnly => quote! {#forwarded_attrs #vis #ident #colon #ty},
547 FieldHandling::IsOption => quote! {#forwarded_attrs #serde_attributes #vis #ident #colon #optioned_ty},
548 FieldHandling::Other => quote! {#forwarded_attrs #serde_attributes #vis #ident #colon Option<#optioned_ty>},
549 })
550 },
551 ).collect::<Result<Vec<_>, _>>()?;
552 Ok(struct_wrapper(fields_token, &fields.struct_type))
553}
554
555fn into_optioned(
559 struct_parsed: &StructParsed,
560 self_selector_fn: impl Fn(&TokenStream) -> TokenStream,
561) -> TokenStream {
562 let fields_token = struct_parsed.fields.iter().enumerate().map(|(i, FieldParsed { field: Field { ident, ty, .. }, handling })| {
563 let colon = ident.as_ref().map(|_| quote! {:});
564 let selector = ident.as_ref().map_or_else(|| {
565 let i = Literal::usize_unsuffixed(i);
566 quote! {#i}
567 }, ToTokens::to_token_stream);
568 let self_selector = self_selector_fn(&selector);
569 let crate_name = &struct_parsed.crate_name;
570 match (handling, is_self_resolving_optioned(ty)) {
571 (FieldHandling::Required, _) | (FieldHandling::IsOption, true) => quote! {#ident #colon #self_selector},
572 (FieldHandling::IsOption, false) => quote! {#ident #colon #crate_name::OptionableConvert::into_optioned(#self_selector)},
573 (FieldHandling::Other, true) => quote! {#ident #colon Some(#self_selector)},
574 (FieldHandling::Other, false) => quote! {#ident #colon Some(#crate_name::OptionableConvert::into_optioned(#self_selector))},
575 (FieldHandling::OptionedOnly, _) => quote! {#ident #colon Default::default()},
576 }
577 });
578 struct_wrapper(fields_token, &struct_parsed.struct_type)
579}
580
581fn try_from_optioned(
585 struct_parsed: &StructParsed,
586 value_selector_fn: impl Fn(&TokenStream) -> TokenStream,
587) -> TokenStream {
588 let fields_token = struct_parsed.fields.iter().enumerate().map(|(i, FieldParsed { field: Field { ident, ty, .. }, handling })| {
589 let colon = ident.as_ref().map(|_| quote! {:});
590 let selector = ident.as_ref().map_or_else(|| {
591 let i = Literal::usize_unsuffixed(i);
592 quote! {#i}
593 }, ToTokens::to_token_stream);
594 let value_selector = value_selector_fn(&selector);
595 let crate_name = &struct_parsed.crate_name;
596 match (handling, is_self_resolving_optioned(ty)) {
597 (FieldHandling::Required, _) | (FieldHandling::IsOption, true) => quote! {#ident #colon value.#selector},
598 (FieldHandling::IsOption, false) => quote! {
599 #ident #colon #crate_name::OptionableConvert::try_from_optioned(
600 #value_selector
601 )?
602 },
603 (FieldHandling::Other, true) => {
604 let selector_quoted = LitStr::new(&selector.to_string(), ident.span());
605 quote! {
606 #ident #colon #value_selector.ok_or(#crate_name::Error{ missing_field: #selector_quoted })?
607 }
608 }
609 (FieldHandling::Other, false) => {
610 let selector_quoted = LitStr::new(&selector.to_string(), ident.span());
611 quote! {
612 #ident #colon #crate_name::OptionableConvert::try_from_optioned(#value_selector.ok_or(#crate_name::Error{ missing_field: #selector_quoted })?
613 )?
614 }
615 }
616 (FieldHandling::OptionedOnly, _) => TokenStream::default(),
617 }
618 });
619
620 struct_wrapper(fields_token, &struct_parsed.struct_type)
621}
622
623fn merge_fields(
629 struct_parsed: &StructParsed,
630 self_selector_fn: impl Fn(&TokenStream) -> TokenStream,
631 other_selector_fn: impl Fn(&TokenStream) -> TokenStream,
632 merge_self_mut: bool,
633) -> TokenStream {
634 let fields_token = struct_parsed.fields.iter().enumerate().map(
635 |(
636 i,
637 FieldParsed {
638 field: Field { ident, ty, .. },
639 handling,
640 },
641 )| {
642 let selector = ident.as_ref().map_or_else(
643 || {
644 let i = Literal::usize_unsuffixed(i);
645 quote! {#i}
646 },
647 ToTokens::to_token_stream,
648 );
649 let self_merge_mut_modifier = merge_self_mut.then(|| quote! {&mut});
650 let deref_modifier = (!merge_self_mut).then(|| quote! {*});
651 let self_selector = self_selector_fn(&selector);
652 let other_selector = other_selector_fn(&selector);
653 let crate_name = &struct_parsed.crate_name;
654 match (handling, is_self_resolving_optioned(ty)) {
655 (FieldHandling::Required, _) | (FieldHandling::IsOption, true) => quote! {#deref_modifier #self_selector = #other_selector;},
656 (FieldHandling::IsOption, false) => quote! {
657 #crate_name::OptionableConvert::merge(#self_merge_mut_modifier #self_selector, #other_selector)?;
658 },
659 (FieldHandling::Other, true) => quote! {
660 if let Some(other_value)=#other_selector{
661 #deref_modifier #self_selector = other_value;
662 }
663 },
664 (FieldHandling::Other, false) => quote! {
665 if let Some(other_value)=#other_selector{
666 #crate_name::OptionableConvert::merge(#self_merge_mut_modifier #self_selector, other_value)?;
667 }
668 },
669 (FieldHandling::OptionedOnly, _) => TokenStream::default(),
670 }
671 },
672 );
673
674 quote! {
675 #(#fields_token)*
676 }
677}
678fn optioned_ty(crate_name: &Path, ty: &Type) -> TokenStream {
683 if is_self_resolving_optioned(ty) {
684 ty.to_token_stream()
685 } else {
686 quote! { <#ty as #crate_name::Optionable>::Optioned }
687 }
688}
689
690const SELF_RESOLVING_TYPES: [&str; 18] = [
691 "i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64", "u128", "usize", "f32",
693 "f64", "char", "bool", "String", "OsString",
695];
696
697fn is_self_resolving_optioned(ty: &Type) -> bool {
700 if let Type::Path(TypePath { qself, path }) = &ty
701 && qself.is_none()
702 && SELF_RESOLVING_TYPES.contains(&&*path.to_token_stream().to_string())
703 {
704 true
705 } else {
706 false
707 }
708}
709
710fn forwarded_attributes(
713 attrs: &[Attribute],
714 attr_to_copy: &HashMap<Path, HashSet<Path>>,
715) -> Result<Option<TokenStream>, Error> {
716 let forward_attrs = attrs
717 .iter()
718 .map(|attr| {
719 if attr.path().is_ident(HELPER_ATTR_IDENT) {
720 return match &attr.meta {
721 Meta::List(MetaList { tokens, .. }) => Ok(Some(quote!(#[#tokens]))),
722 _ => error("Only lists like `#[optionable_attr(Serialize,Deserialize)]` are supported for `optionable_attr`"),
723 };
724 }
725 let keys_to_copy=attr_to_copy.get(attr.path());
726 if let Some(keys_to_copy)=keys_to_copy{
727 if keys_to_copy.is_empty(){
729 Ok(Some(attr.to_token_stream()))
730 } else{
731 match &attr.meta{
732 Meta::Path(_) => Ok(None),
733 Meta::NameValue(meta_name_value) => Ok(keys_to_copy.contains(&meta_name_value.path).then(|| attr.to_token_stream())),
734 Meta::List(meta_list) => {
735 let inner_metas :Vec<TokenStream>= syn::punctuated::Punctuated::<Meta, syn::Token![,]>::parse_terminated
737 .parse2(meta_list.tokens.clone())?.into_iter().filter_map(|meta|{
738 if let Meta::NameValue(meta_name_value)= meta{
739 keys_to_copy.contains(&meta_name_value.path).then(|| Ok::<_,Error>(meta_name_value.to_token_stream()))
740 } else{
741 None
742 }
743 }).collect::<Result<_,_>>()?;
744 if inner_metas.is_empty() {
745 Ok(None)
746 } else {
747 let mut attr=attr.clone();
748 let mut meta_list=meta_list.clone();
749 meta_list.tokens=inner_metas.into_iter().collect();
750 attr.meta=meta_list.into();
751 Ok(Some(attr.to_token_stream()))
752 }
753 }
754 }
755 }
756 } else{
757 Ok(None)
758 }
759 })
760 .collect::<Result<Vec<_>,_>>()?
761 .into_iter()
762 .flatten()
763 .collect::<TokenStream>();
764 Ok((!forward_attrs.is_empty()).then_some(forward_attrs))
765}
766
767#[cfg(test)]
768mod tests {
769 use crate::{derive_optionable, CodegenSettings};
770 use darling::FromMeta;
771 use proc_macro2::TokenStream;
772 use quote::quote;
773 use syn::{parse_quote, Path};
774
775 struct TestCase {
776 input: TokenStream,
777 output: TokenStream,
778 }
779
780 #[test]
781 #[allow(clippy::too_many_lines)]
782 fn test_optionable() {
783 let tcs = vec![
784 TestCase {
786 input: quote! {
787 #[derive(Optionable)]
788 struct DeriveExample {
789 name: String,
790 pub surname: String,
791 }
792 },
793 output: quote! {
794 struct DeriveExampleOpt {
795 name: Option<String>,
796 pub surname: Option<String>
797 }
798
799 #[automatically_derived]
800 impl ::optionable::Optionable for DeriveExample {
801 type Optioned = DeriveExampleOpt;
802 }
803
804 #[automatically_derived]
805 impl ::optionable::Optionable for DeriveExampleOpt {
806 type Optioned = DeriveExampleOpt;
807 }
808
809 #[automatically_derived]
810 impl ::optionable::OptionableConvert for DeriveExample {
811 fn into_optioned (self) -> DeriveExampleOpt {
812 DeriveExampleOpt {
813 name: Some(self.name),
814 surname:Some(self.surname)
815 }
816 }
817
818 fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::Error> {
819 Ok(Self{
820 name: value.name.ok_or(::optionable::Error { missing_field: "name" })?,
821 surname: value.surname.ok_or(::optionable::Error { missing_field: "surname" })?
822 })
823 }
824
825 fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::Error> {
826 if let Some(other_value) = other.name {
827 self.name = other_value;
828 }
829 if let Some(other_value) = other.surname {
830 self.surname = other_value;
831 }
832 Ok(())
833 }
834 }
835 },
836 },
837 TestCase {
839 input: quote! {
840 #[derive(Optionable)]
841 #[optionable(no_convert)]
842 struct DeriveExample {
843 name: String,
844 pub surname: String,
845 }
846 },
847 output: quote! {
848 struct DeriveExampleOpt {
849 name: Option<String>,
850 pub surname: Option<String>
851 }
852
853 #[automatically_derived]
854 impl ::optionable::Optionable for DeriveExample {
855 type Optioned = DeriveExampleOpt;
856 }
857
858 #[automatically_derived]
859 impl ::optionable::Optionable for DeriveExampleOpt {
860 type Optioned = DeriveExampleOpt;
861 }
862 },
863 },
864 TestCase {
866 input: quote! {
867 #[derive(Optionable)]
868 struct DeriveExample {
869 name: String,
870 #[optionable(required)]
871 pub surname: String,
872 }
873 },
874 output: quote! {
875 struct DeriveExampleOpt {
876 name: Option<String>,
877 pub surname: String
878 }
879
880 #[automatically_derived]
881 impl ::optionable::Optionable for DeriveExample {
882 type Optioned = DeriveExampleOpt;
883 }
884
885 #[automatically_derived]
886 impl ::optionable::Optionable for DeriveExampleOpt {
887 type Optioned = DeriveExampleOpt;
888 }
889
890 #[automatically_derived]
891 impl ::optionable::OptionableConvert for DeriveExample {
892 fn into_optioned (self) -> DeriveExampleOpt {
893 DeriveExampleOpt {
894 name: Some(self.name),
895 surname: self.surname
896 }
897 }
898
899 fn try_from_optioned(value:DeriveExampleOpt ) -> Result <Self, ::optionable::Error> {
900 Ok (Self {
901 name: value.name.ok_or(::optionable::Error { missing_field: "name" })?,
902 surname: value.surname
903 })
904 }
905
906 fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::Error> {
907 if let Some(other_value) = other.name {
908 self.name = other_value;
909 }
910 self.surname = other.surname;
911 Ok (())
912 }
913 }
914 },
915 },
916 TestCase {
918 input: quote! {
919 #[derive(Optionable)]
920 #[optionable(derive(Deserialize,Serialize,Default),suffix="Ac")]
921 #[optionable(attr_copy(attr=serde,key=rename))]
922 #[optionable_attr(serde(rename_all = "camelCase", deny_unknown_fields))]
923 #[optionable_attr(serde(default))]
924 struct DeriveExample {
925 #[optionable_attr(serde(rename = "firstName"))]
926 name: String,
927 #[serde(rename="middle__name")]
928 middle_name: Option<String>,
929 surname: String,
930 }
931 },
932 output: quote! {
933 #[derive(Deserialize, Serialize,Default)]
934 #[serde(rename_all = "camelCase", deny_unknown_fields)]
935 #[serde(default)]
936 struct DeriveExampleAc {
937 #[serde(rename = "firstName")]
938 #[serde(skip_serializing_if = "Option::is_none")]
939 name: Option<String>,
940 #[serde(rename = "middle__name")]
941 #[serde(skip_serializing_if = "Option::is_none")]
942 middle_name: <Option<String> as ::optionable::Optionable>::Optioned,
943 #[serde(skip_serializing_if = "Option::is_none")]
944 surname: Option<String>
945 }
946
947 #[automatically_derived]
948 impl ::optionable::Optionable for DeriveExample {
949 type Optioned = DeriveExampleAc;
950 }
951
952 #[automatically_derived]
953 impl ::optionable::Optionable for DeriveExampleAc {
954 type Optioned = DeriveExampleAc;
955 }
956
957 #[automatically_derived]
958 impl ::optionable::OptionableConvert for DeriveExample {
959 fn into_optioned (self) -> DeriveExampleAc {
960 DeriveExampleAc {
961 name: Some(self.name),
962 middle_name: ::optionable::OptionableConvert::into_optioned(self.middle_name),
963 surname: Some(self.surname)
964 }
965 }
966
967 fn try_from_optioned(value: DeriveExampleAc ) -> Result <Self, ::optionable::Error> {
968 Ok(Self{
969 name: value.name.ok_or(::optionable::Error { missing_field: "name"})?,
970 middle_name: ::optionable::OptionableConvert::try_from_optioned(value.middle_name)?,
971 surname: value.surname.ok_or(::optionable::Error { missing_field: "surname"})?
972 })
973 }
974
975 fn merge(&mut self, other: DeriveExampleAc ) -> Result<(), ::optionable::Error> {
976 if let Some(other_value) = other.name {
977 self.name = other_value;
978 }
979 ::optionable::OptionableConvert::merge(&mut self.middle_name, other.middle_name)?;
980 if let Some(other_value) = other.surname {
981 self.surname = other_value;
982 }
983 Ok(())
984 }
985 }
986 },
987 },
988 TestCase {
990 input: quote! {
991 #[derive(Optionable)]
992 #[optionable(derive(serde::Deserialize,serde::Serialize),suffix="Ac")]
993 struct DeriveExample {
994 name: String,
995 surname: String,
996 }
997 },
998 output: quote! {
999 #[derive(serde::Deserialize, serde::Serialize)]
1000 struct DeriveExampleAc {
1001 #[serde(skip_serializing_if = "Option::is_none")]
1002 name: Option<String>,
1003 #[serde(skip_serializing_if = "Option::is_none")]
1004 surname: Option<String>
1005 }
1006
1007 #[automatically_derived]
1008 impl ::optionable::Optionable for DeriveExample {
1009 type Optioned = DeriveExampleAc;
1010 }
1011
1012 #[automatically_derived]
1013 impl ::optionable::Optionable for DeriveExampleAc {
1014 type Optioned = DeriveExampleAc;
1015 }
1016
1017 #[automatically_derived]
1018 impl ::optionable::OptionableConvert for DeriveExample {
1019 fn into_optioned (self) -> DeriveExampleAc {
1020 DeriveExampleAc {
1021 name: Some(self.name),
1022 surname: Some(self.surname)
1023 }
1024 }
1025
1026 fn try_from_optioned(value: DeriveExampleAc ) -> Result <Self, ::optionable::Error> {
1027 Ok(Self{
1028 name: value.name.ok_or(::optionable::Error{ missing_field: "name"})?,
1029 surname: value.surname.ok_or(::optionable::Error{ missing_field: "surname"})?
1030 })
1031 }
1032
1033 fn merge(&mut self, other: DeriveExampleAc ) -> Result<(), ::optionable::Error> {
1034 if let Some(other_value) = other.name {
1035 self.name = other_value;
1036 }
1037 if let Some(other_value) = other.surname {
1038 self.surname = other_value;
1039 }
1040 Ok(())
1041 }
1042 }
1043 },
1044 },
1045 TestCase {
1047 input: quote! {
1048 #[derive(Optionable)]
1049 struct DeriveExample(pub String, i32);
1050 },
1051 output: quote! {
1052 struct DeriveExampleOpt(
1053 pub Option<String>,
1054 Option<i32>
1055 );
1056
1057 #[automatically_derived]
1058 impl ::optionable::Optionable for DeriveExample {
1059 type Optioned = DeriveExampleOpt;
1060 }
1061
1062 #[automatically_derived]
1063 impl ::optionable::Optionable for DeriveExampleOpt {
1064 type Optioned = DeriveExampleOpt;
1065 }
1066
1067 #[automatically_derived]
1068 impl ::optionable::OptionableConvert for DeriveExample {
1069 fn into_optioned (self) -> DeriveExampleOpt {
1070 DeriveExampleOpt (
1071 Some(self.0),
1072 Some(self.1)
1073 )
1074 }
1075
1076 fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::Error> {
1077 Ok(Self(
1078 value.0.ok_or(::optionable::Error { missing_field:"0" })?,
1079 value.1.ok_or(::optionable::Error { missing_field: "1" })?
1080 ))
1081 }
1082
1083 fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::Error> {
1084 if let Some(other_value) = other.0 {
1085 self.0 = other_value;
1086 }
1087 if let Some (other_value) = other.1 {
1088 self.1 = other_value;
1089 }
1090 Ok (())
1091 }
1092 }
1093 },
1094 },
1095 TestCase {
1097 input: quote! {
1098 #[derive(Optionable)]
1099 struct DeriveExample(pub String, #[optionable(required)] i32);
1100 },
1101 output: quote! {
1102 struct DeriveExampleOpt(
1103 pub Option<String>,
1104 i32
1105 );
1106
1107 #[automatically_derived]
1108 impl ::optionable::Optionable for DeriveExample {
1109 type Optioned = DeriveExampleOpt;
1110 }
1111
1112 #[automatically_derived]
1113 impl ::optionable::Optionable for DeriveExampleOpt {
1114 type Optioned = DeriveExampleOpt;
1115 }
1116
1117 # [automatically_derived]
1118 impl ::optionable::OptionableConvert for DeriveExample {
1119 fn into_optioned (self) -> DeriveExampleOpt {
1120 DeriveExampleOpt (
1121 Some(self.0),
1122 self.1
1123 )
1124 }
1125
1126 fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::Error> {
1127 Ok(Self(
1128 value.0.ok_or(::optionable::Error { missing_field: "0" })?,
1129 value.1))
1130 }
1131
1132 fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::Error> {
1133 if let Some(other_value) = other.0 {
1134 self.0 = other_value;
1135 }
1136 self.1 = other.1;
1137 Ok (())
1138 }
1139 }
1140 },
1141 },
1142 TestCase {
1144 input: quote! {
1145 #[derive(Optionable)]
1146 #[optionable(derive(Serialize, Deserialize))]
1147 struct DeriveExample<T, T2: Serialize, T3> {
1148 output: T,
1149 input: T2,
1150 #[optionable(required)]
1151 extra: T3,
1152 }
1153 },
1154 output: quote! {
1155 #[derive (Serialize , Deserialize)]
1156 struct DeriveExampleOpt<T, T2: Serialize, T3>
1157 where T: ::optionable::Optionable,
1158 <T as ::optionable::Optionable>::Optioned: Sized + Serialize + serde::de::DeserializeOwned,
1159 T2: ::optionable::Optionable,
1160 <T2 as ::optionable::Optionable>::Optioned: Sized + Serialize + serde::de::DeserializeOwned
1161 {
1162 #[serde(skip_serializing_if="Option::is_none")]
1163 output: Option< <T as ::optionable::Optionable>::Optioned>,
1164 #[serde(skip_serializing_if="Option::is_none")]
1165 input: Option< <T2 as ::optionable::Optionable>::Optioned>,
1166 extra: T3
1167 }
1168
1169 #[automatically_derived]
1170 impl<T, T2: Serialize, T3> ::optionable::Optionable for DeriveExample<T, T2, T3>
1171 where T: ::optionable::Optionable,
1172 <T as ::optionable::Optionable>::Optioned: Sized + Serialize + serde::de::DeserializeOwned,
1173 T2: ::optionable::Optionable,
1174 <T2 as ::optionable::Optionable>::Optioned: Sized + Serialize + serde::de::DeserializeOwned
1175 {
1176 type Optioned = DeriveExampleOpt<T,T2,T3>;
1177 }
1178
1179 #[automatically_derived]
1180 impl<T, T2: Serialize, T3> ::optionable::Optionable for DeriveExampleOpt<T, T2, T3>
1181 where T: ::optionable::Optionable,
1182 <T as ::optionable::Optionable>::Optioned: Sized + Serialize + serde::de::DeserializeOwned,
1183 T2: ::optionable::Optionable,
1184 <T2 as ::optionable::Optionable>::Optioned: Sized + Serialize + serde::de::DeserializeOwned
1185 {
1186 type Optioned = DeriveExampleOpt<T, T2, T3>;
1187 }
1188
1189 #[automatically_derived]
1190 impl <T, T2:Serialize, T3> ::optionable::OptionableConvert for DeriveExample<T, T2, T3>
1191 where T: ::optionable::OptionableConvert,
1192 <T as ::optionable::Optionable>::Optioned: Sized + Serialize + serde::de::DeserializeOwned,
1193 T2: ::optionable::OptionableConvert,
1194 <T2 as ::optionable::Optionable>::Optioned: Sized + Serialize + serde::de::DeserializeOwned
1195 {
1196 fn into_optioned (self) -> DeriveExampleOpt<T, T2, T3> {
1197 DeriveExampleOpt::<T, T2, T3> {
1198 output: Some(::optionable::OptionableConvert::into_optioned(self.output)),
1199 input: Some(::optionable::OptionableConvert::into_optioned(self.input)),
1200 extra: self.extra
1201 }
1202 }
1203
1204 fn try_from_optioned(value: DeriveExampleOpt<T, T2, T3> ) -> Result <Self, ::optionable::Error> {
1205 Ok(Self{
1206 output: ::optionable::OptionableConvert::try_from_optioned(value.output.ok_or(::optionable::Error { missing_field: "output" })?)?,
1207 input: ::optionable::OptionableConvert::try_from_optioned(value.input.ok_or(::optionable::Error { missing_field: "input" })?)?,
1208 extra: value.extra
1209 })
1210 }
1211
1212 fn merge(&mut self, other: DeriveExampleOpt<T, T2, T3> ) -> Result<(), ::optionable::Error> {
1213 if let Some(other_value) = other.output {
1214 ::optionable::OptionableConvert::merge(&mut self.output, other_value)?;
1215 }
1216 if let Some(other_value) = other.input {
1217 ::optionable::OptionableConvert::merge(&mut self.input, other_value)?;
1218 }
1219 self.extra=other.extra;
1220 Ok(())
1221 }
1222 }
1223 },
1224 },
1225 TestCase {
1226 input: quote! {
1227 #[derive(Optionable)]
1228 enum DeriveExample {
1229 Unit,
1230 Plain(String),
1231 Address{street: String, number: u32},
1232 Address2(String,u32),
1233 }
1234 },
1235 output: quote! {
1236 enum DeriveExampleOpt {
1237 Unit,
1238 Plain( Option<String> ),
1239 Address{ street: Option<String>, number:Option<u32> },
1240 Address2( Option<String>, Option<u32> )
1241 }
1242
1243 #[automatically_derived]
1244 impl ::optionable::Optionable for DeriveExample {
1245 type Optioned = DeriveExampleOpt;
1246 }
1247
1248 #[automatically_derived]
1249 impl ::optionable::Optionable for DeriveExampleOpt {
1250 type Optioned = DeriveExampleOpt;
1251 }
1252
1253 #[automatically_derived]
1254 impl ::optionable::OptionableConvert for DeriveExample {
1255 fn into_optioned (self) -> DeriveExampleOpt {
1256 match self{
1257 Self::Unit => DeriveExampleOpt::Unit,
1258 Self::Plain(self_0) => DeriveExampleOpt::Plain(
1259 Some(self_0)
1260 ),
1261 Self::Address{street: self_street, number: self_number} => DeriveExampleOpt::Address{
1262 street: Some(self_street),
1263 number: Some(self_number)
1264 },
1265 Self::Address2(self_0, self_1) => DeriveExampleOpt::Address2(
1266 Some(self_0),
1267 Some(self_1)
1268 )
1269 }
1270 }
1271
1272 fn try_from_optioned(other: DeriveExampleOpt) -> Result <Self, ::optionable::Error> {
1273 Ok (match other {
1274 DeriveExampleOpt::Unit => Self::Unit,
1275 DeriveExampleOpt::Plain(other_0) => Self::Plain(
1276 other_0.ok_or(::optionable::Error { missing_field: "0" })?
1277 ),
1278 DeriveExampleOpt::Address{street: other_street, number: other_number} => Self::Address{
1279 street: other_street.ok_or(::optionable::Error { missing_field: "street" })?,
1280 number: other_number.ok_or(::optionable::Error { missing_field: "number" })?
1281 },
1282 DeriveExampleOpt::Address2(other_0, other_1) => Self::Address2(
1283 other_0.ok_or(::optionable::Error { missing_field: "0"})?,
1284 other_1.ok_or(::optionable::Error { missing_field: "1"})?)
1285 })
1286 }
1287
1288 fn merge(&mut self, other: DeriveExampleOpt) -> Result<(), ::optionable::Error> {
1289 match other {
1290 DeriveExampleOpt::Unit => {
1291 if let Self::Unit = self {} else {
1292 *self = Self::try_from_optioned(DeriveExampleOpt::Unit)?;
1293 }
1294 },
1295 DeriveExampleOpt::Plain(other_0) => {
1296 if let Self::Plain(self_0) = self{
1297 if let Some(other_value) = other_0 {
1298 *self_0 = other_value;
1299 }
1300 } else {
1301 *self = Self::try_from_optioned(DeriveExampleOpt::Plain(other_0))?;
1302 }
1303 },
1304 DeriveExampleOpt::Address{street: other_street, number: other_number} => {
1305 if let Self::Address{street: self_street, number: self_number} = self{
1306 if let Some(other_value) = other_street {
1307 *self_street = other_value;
1308 }
1309 if let Some(other_value) = other_number {
1310 *self_number = other_value;
1311 }
1312 } else {
1313 *self = Self::try_from_optioned(DeriveExampleOpt::Address{street: other_street, number: other_number})?;
1314 }
1315 },
1316 DeriveExampleOpt::Address2(other_0, other_1) => {
1317 if let Self::Address2(self_0, self_1) = self{
1318 if let Some(other_value) = other_0 {
1319 *self_0 = other_value;
1320 }
1321 if let Some(other_value) = other_1 {
1322 *self_1 = other_value;
1323 }
1324 } else {
1325 *self = Self::try_from_optioned(DeriveExampleOpt::Address2(other_0, other_1))?;
1326 }
1327 }
1328 }
1329 Ok(())
1330 }
1331 }
1332 },
1333 },
1334 ];
1335 for tc in tcs {
1336 let input = syn::parse2(tc.input).unwrap();
1337 let output = derive_optionable(input, None).unwrap();
1338 println!("{output}");
1339 assert_eq!(tc.output.to_string(), output.to_string());
1340 }
1341 }
1342
1343 #[test]
1344 #[allow(clippy::too_many_lines)]
1345 fn test_crate_replacement() {
1347 let tcs = vec![TestCase {
1348 input: quote! {
1349 #[derive(Optionable)]
1350 struct DeriveExample {
1351 name: crate::Name,
1352 pub surname: Box<crate::SurName>,
1353 }
1354 },
1355 output: quote! {
1356 struct DeriveExampleOpt {
1357 name: Option< <::testcrate::Name as crate::Optionable>::Optioned>,
1358 pub surname: Option< <Box<::testcrate::SurName> as crate::Optionable>::Optioned>
1359 }
1360
1361 #[automatically_derived]
1362 impl crate::Optionable for crate_prefix::DeriveExample {
1363 type Optioned = DeriveExampleOpt;
1364 }
1365
1366 #[automatically_derived]
1367 impl crate::Optionable for DeriveExampleOpt {
1368 type Optioned = DeriveExampleOpt;
1369 }
1370
1371 #[automatically_derived]
1372 impl crate::OptionableConvert for crate_prefix::DeriveExample {
1373 fn into_optioned (self) -> DeriveExampleOpt {
1374 DeriveExampleOpt {
1375 name: Some(crate::OptionableConvert::into_optioned(self.name)),
1376 surname: Some(crate::OptionableConvert::into_optioned(self.surname))
1377 }
1378 }
1379
1380 fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, crate::Error> {
1381 Ok(Self{
1382 name: crate::OptionableConvert::try_from_optioned(value.name.ok_or(crate::Error { missing_field: "name" })?)?,
1383 surname: crate::OptionableConvert::try_from_optioned(value.surname.ok_or(crate::Error { missing_field: "surname" })?)?
1384 })
1385 }
1386
1387 fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), crate::Error> {
1388 if let Some(other_value) = other.name {
1389 crate::OptionableConvert::merge(&mut self.name, other_value)?;
1390 }
1391 if let Some(other_value) = other.surname {
1392 crate::OptionableConvert::merge(&mut self.surname, other_value)?;
1393 }
1394 Ok(())
1395 }
1396 }
1397 },
1398 }];
1399 for tc in tcs {
1400 let input = syn::parse2(tc.input).unwrap();
1401 let output = derive_optionable(
1402 input,
1403 Some(&CodegenSettings {
1404 ty_prefix: Some(Path::from_string("crate_prefix").unwrap()),
1405 optionable_crate_name: Path::from_string("crate").unwrap(),
1406 input_crate_replacement: Some(parse_quote!(testcrate)),
1407 }),
1408 )
1409 .unwrap();
1410 assert_eq!(tc.output.to_string(), output.to_string());
1411 }
1412 }
1413}