1use crate::FieldHandling::{IsOption, Other, Required};
13use darling::util::PathList;
14use darling::{FromAttributes, FromDeriveInput};
15use itertools::MultiUnzip;
16use proc_macro2::{Ident, Literal, Span, TokenStream};
17use quote::{format_ident, quote, ToTokens};
18use std::cmp::PartialEq;
19use std::default::Default;
20use std::{fmt, iter};
21use syn::punctuated::Punctuated;
22use syn::spanned::Spanned;
23use syn::token::Where;
24use syn::{
25 parse_quote, Attribute, Data, DeriveInput, Error, Field, Fields, GenericParam, LitStr, Meta, MetaList,
26 Path, Token, Type, TypePath, WhereClause, WherePredicate,
27};
28
29const HELPER_IDENT: &str = "optionable";
30const HELPER_ATTR_IDENT: &str = "optionable_attr";
31const ERR_MSG_HELPER_ATTR_ENUM_VARIANTS: &str =
32 "#[optionable] helper attributes not supported on enum variant level.";
33
34#[derive(FromDeriveInput)]
35#[darling(attributes(optionable))]
36struct TypeHelperAttributes {
38 derive: Option<PathList>,
40 #[darling(default=default_suffix)]
41 suffix: LitStr,
43 no_convert: Option<()>,
45}
46
47#[derive(FromAttributes)]
48#[darling(attributes(optionable))]
49struct FieldHelperAttributes {
51 required: Option<()>,
53}
54
55fn default_suffix() -> LitStr {
56 LitStr::new("Opt", Span::call_site())
57}
58
59#[must_use]
61pub fn attribute_no_convert() -> Attribute {
62 parse_quote!(#[optionable(no_convert)])
63}
64
65#[must_use]
67pub fn attribute_suffix(suffix: &str) -> Attribute {
68 parse_quote!(#[optionable(suffix=#suffix)])
69}
70
71#[must_use]
74pub fn attribute_derives(derives: &PathList) -> Attribute {
75 parse_quote!(#[optionable(derive(#(#derives),*))])
76}
77
78#[allow(clippy::too_many_lines)]
84pub fn derive_optionable(input: DeriveInput) -> syn::Result<TokenStream> {
85 let attrs = TypeHelperAttributes::from_derive_input(&input)?;
86 let forward_attrs = forwarded_attributes(&input.attrs);
87 let vis = input.vis;
88 let type_ident_opt = Ident::new(
89 &(input.ident.to_string() + &attrs.suffix.value()),
90 input.ident.span(),
91 );
92 let type_ident = &input.ident;
93
94 let generics_colon = (!input.generics.params.is_empty()).then(|| quote! {::});
95 let (impl_generics, ty_generics, _) = input.generics.split_for_impl();
96 let mut where_clause = input
97 .generics
98 .where_clause
99 .clone()
100 .unwrap_or_else(|| WhereClause {
101 where_token: Where::default(),
102 predicates: Punctuated::default(),
103 });
104 let where_clause_convert = attrs.no_convert.is_none().then(|| {
105 let mut where_clause = where_clause.clone();
106
107 patch_where_clause_bounds(&mut where_clause, &input.generics.params, |_| {
108 quote!(::optionable::OptionableConvert)
109 });
110 where_clause
111 });
112 patch_where_clause_bounds(&mut where_clause, &input.generics.params, |_| {
113 quote!(::optionable::Optionable)
114 });
115
116 let impls_optionable = quote! {
119 #[automatically_derived]
120 impl #impl_generics ::optionable::Optionable for #type_ident #ty_generics #where_clause {
121 type Optioned = #type_ident_opt #ty_generics;
122 }
123
124 #[automatically_derived]
125 impl #impl_generics ::optionable::Optionable for #type_ident_opt #ty_generics #where_clause {
126 type Optioned = #type_ident_opt #ty_generics;
127 }
128 };
129
130 let derives = attrs.derive.unwrap_or_default();
133 let skip_optionable_if_serde_serialize = derives
134 .iter()
135 .any(is_serialize)
136 .then(|| quote!(#[serde(skip_serializing_if = "Option::is_none")]));
137 let derives = (!derives
138 .iter()
139 .map(ToTokens::to_token_stream)
140 .collect::<Vec<_>>()
141 .is_empty())
142 .then(|| quote! {#[derive(#(#derives),*)]});
143
144 match input.data {
145 Data::Struct(s) => {
146 let fields = into_field_handling(s.fields)?;
147 let unnamed_struct_semicolon =
148 (fields.struct_type == StructType::Unnamed).then(|| quote!(;));
149 let optioned_fields =
150 optioned_fields(&fields, skip_optionable_if_serde_serialize.as_ref());
151
152 let impl_optionable_convert = attrs.no_convert.is_none().then(|| {
153 let into_optioned_fields = into_optioned(&fields, |selector| quote! { self.#selector });
154 let try_from_optioned_fields =
155 try_from_optioned(&fields, |selector| quote! { value.#selector });
156 let merge_fields = merge_fields(
157 &fields,
158 |selector| quote! { self.#selector },
159 |selector| quote! { other.#selector },
160 true);
161 quote! {
162 #[automatically_derived]
163 impl #impl_generics ::optionable::OptionableConvert for #type_ident #ty_generics #where_clause_convert {
164 fn into_optioned(self) -> #type_ident_opt #ty_generics {
165 #type_ident_opt #generics_colon #ty_generics #into_optioned_fields
166 }
167
168 fn try_from_optioned(value: #type_ident_opt #ty_generics) -> Result<Self,::optionable::optionable::Error>{
169 Ok(Self #try_from_optioned_fields)
170 }
171
172 fn merge(&mut self, other: #type_ident_opt #ty_generics) -> Result<(), ::optionable::optionable::Error>{
173 #merge_fields
174 Ok(())
175 }
176 }
177 }
178 });
179
180 Ok(quote! {
181 #[automatically_derived]
182 #derives
183 #forward_attrs
184 #vis struct #type_ident_opt #impl_generics #where_clause #optioned_fields #unnamed_struct_semicolon
185
186 #impls_optionable
187
188 #impl_optionable_convert
189 })
190 }
191 Data::Enum(e) => {
192 let self_prefix = quote! {self_};
193 let other_prefix = quote! {other_};
194
195 let variants = e
196 .variants
197 .into_iter()
198 .map(|v| {
199 error_on_helper_attributes(&v.attrs, ERR_MSG_HELPER_ATTR_ENUM_VARIANTS)?;
200 Ok::<_, Error>((
201 v.ident,
202 forwarded_attributes(&v.attrs),
203 into_field_handling(v.fields)?,
204 ))
205 })
206 .collect::<Result<Vec<_>, _>>()?;
207
208 let optioned_variants = variants.iter().map(|(variant, forward_attrs, f)| {
209 let fields = optioned_fields(f, skip_optionable_if_serde_serialize.as_ref());
210 quote!( #forward_attrs #variant #fields )
211 });
212
213 let impl_optionable_convert = attrs.no_convert.is_none().then(|| {
214 let (into_variants, try_from_variants, merge_variants): (Vec<_>, Vec<_>, Vec<_>) = variants
215 .iter()
216 .map(|(variant,_, f)| {
217 let fields_into = into_optioned(f, |selector| {
218 format_ident!("{self_prefix}{selector}").to_token_stream()
219 });
220 let fields_try_from = try_from_optioned(f, |selector| {
221 format_ident!("{other_prefix}{selector}").to_token_stream()
222 });
223 let fields_merge = merge_fields(f,
224 |selector| format_ident!("{self_prefix}{selector}").to_token_stream(),
225 |selector| format_ident!("{other_prefix}{selector}").to_token_stream(),
226 false);
227 let self_destructure = destructure(f, &self_prefix)?;
228 let other_destructure = destructure(f, &other_prefix)?;
229 Ok::<_, Error>((
230 quote!( Self::#variant #self_destructure => #type_ident_opt::#variant #fields_into ),
231 quote!( #type_ident_opt::#variant #other_destructure => Self::#variant #fields_try_from ),
232 quote!( #type_ident_opt::#variant #other_destructure => {
233 if let Self::#variant #self_destructure = self {
234 #fields_merge
235 } else {
236 *self = Self::try_from_optioned(#type_ident_opt::#variant #other_destructure)?;
237 }
238 })
239 ))
240 })
241 .collect::<Result<Vec<_>, _>>()?
242 .into_iter().multiunzip();
243 Ok::<_, Error>(quote! {
244 #[automatically_derived]
245 impl #impl_generics ::optionable::OptionableConvert for #type_ident #ty_generics #where_clause_convert {
246 fn into_optioned(self) -> #type_ident_opt #ty_generics {
247 match self {
248 #(#into_variants),*
249 }
250 }
251
252 fn try_from_optioned(other: #type_ident_opt #ty_generics)->Result<Self,::optionable::optionable::Error>{
253 Ok(match other{
254 #(#try_from_variants),*
255 })
256 }
257
258 fn merge(&mut self, other: #type_ident_opt #ty_generics) -> Result<(), ::optionable::optionable::Error>{
259 match other{
260 #(#merge_variants),*
261 }
262 Ok(())
263 }
264 }
265 })
266 }).transpose()?;
267
268 Ok(quote!(
269 #[automatically_derived]
270 #derives
271 #forward_attrs
272 #vis enum #type_ident_opt #impl_generics #where_clause {
273 #(#optioned_variants),*
274 }
275
276 #impls_optionable
277
278 #impl_optionable_convert
279 ))
280 }
281 Data::Union(_) => error("#[derive(Optionable)] not supported for unit structs"),
282 }
283}
284
285fn destructure(fields: &FieldsParsed, prefix: &TokenStream) -> Result<TokenStream, Error> {
288 Ok(match fields.struct_type {
289 StructType::Named => {
290 let fields = fields
291 .fields
292 .iter()
293 .map(|f| {
294 let ident = f.field.ident.as_ref().ok_or::<Error>(
295 error::<_, Error>(format!(
296 "expected field name but none present for {f:?}"
297 ))
298 .unwrap_err(),
299 )?;
300 let prefixed_ident = format_ident!("{1}{0}", ident.clone(), prefix.to_string());
301 Ok::<_, Error>(quote! {#ident: #prefixed_ident})
302 })
303 .collect::<Result<Vec<_>, _>>()?;
304 quote! {{#(#fields),*}}
305 }
306 StructType::Unnamed => {
307 let indices: Vec<_> = (0..fields.fields.len())
308 .map(|i| {
309 let prefixed = format_ident!("{1}{0}", i, prefix.to_string());
310 quote! {#prefixed}
311 })
312 .collect();
313 quote! {(#(#indices),*)}
314 }
315 StructType::Unit => quote! {},
316 })
317}
318
319fn optioned_fields(fields: &FieldsParsed, serde_attributes: Option<&TokenStream>) -> TokenStream {
323 let fields_token = fields.fields.iter().map(
324 |FieldParsed {
325 field: Field { attrs,vis, ident, ty, .. },
326 handling,
327 }| {
328 let forward_attrs = forwarded_attributes(attrs);
329 let optioned_ty = optioned_ty(ty);
330 let colon = ident.as_ref().map(|_| quote! {:});
331 match handling {
332 Required => quote! {#forward_attrs #vis #ident #colon #ty},
333 IsOption => quote! {#forward_attrs #serde_attributes #vis #ident #colon #optioned_ty},
334 Other => quote! {#forward_attrs #serde_attributes #vis #ident #colon Option<#optioned_ty>},
335 }
336 },
337 );
338 struct_wrapper(fields_token, &fields.struct_type)
339}
340
341fn into_optioned(
345 fields: &FieldsParsed,
346 self_selector_fn: impl Fn(&TokenStream) -> TokenStream,
347) -> TokenStream {
348 let fields_token = fields.fields.iter().enumerate().map(|(i, FieldParsed { field: Field { ident, ty, .. }, handling })| {
349 let colon = ident.as_ref().map(|_| quote! {:});
350 let selector = ident.as_ref().map_or_else(|| {
351 let i = Literal::usize_unsuffixed(i);
352 quote! {#i}
353 }, ToTokens::to_token_stream);
354 let self_selector = self_selector_fn(&selector);
355 match (handling, is_self_resolving_optioned(ty)) {
356 (Required, _) | (IsOption, true) => quote! {#ident #colon #self_selector},
357 (IsOption, false) => quote! {#ident #colon <#ty as ::optionable::OptionableConvert>::into_optioned(#self_selector)},
358 (Other, true) => quote! {#ident #colon Some(#self_selector)},
359 (Other, false) => quote! {#ident #colon Some(<#ty as ::optionable::OptionableConvert>::into_optioned(#self_selector))}
360 }
361 });
362 struct_wrapper(fields_token, &fields.struct_type)
363}
364
365fn try_from_optioned(
369 fields: &FieldsParsed,
370 value_selector_fn: impl Fn(&TokenStream) -> TokenStream,
371) -> TokenStream {
372 let fields_token = fields.fields.iter().enumerate().map(|(i, FieldParsed { field: Field { ident, ty, .. }, handling })| {
373 let colon = ident.as_ref().map(|_| quote! {:});
374 let selector = ident.as_ref().map_or_else(|| {
375 let i = Literal::usize_unsuffixed(i);
376 quote! {#i}
377 }, ToTokens::to_token_stream);
378 let value_selector = value_selector_fn(&selector);
379 match (handling, is_self_resolving_optioned(ty)) {
380 (Required, _) | (IsOption, true) => quote! {#ident #colon value.#selector},
381 (IsOption, false) => quote! {
382 #ident #colon <#ty as ::optionable::OptionableConvert>::try_from_optioned(
383 #value_selector
384 )?
385 },
386 (Other, true) => {
387 let selector_quoted = LitStr::new(&selector.to_string(), ident.span());
388 quote! {
389 #ident #colon #value_selector.ok_or(optionable::optionable::Error{ missing_fields: std::vec![#selector_quoted] })?
390 }
391 }
392 (Other, false) => {
393 let selector_quoted = LitStr::new(&selector.to_string(), ident.span());
394 quote! {
395 #ident #colon <#ty as ::optionable::OptionableConvert>::try_from_optioned(
396 #value_selector.ok_or(optionable::optionable::Error{ missing_fields: std::vec![#selector_quoted] })?
397 )?
398 }
399 }
400 }
401 });
402
403 struct_wrapper(fields_token, &fields.struct_type)
404}
405
406fn merge_fields(
412 fields: &FieldsParsed,
413 self_selector_fn: impl Fn(&TokenStream) -> TokenStream,
414 other_selector_fn: impl Fn(&TokenStream) -> TokenStream,
415 merge_self_mut: bool,
416) -> TokenStream {
417 let fields_token = fields.fields.iter().enumerate().map(
418 |(
419 i,
420 FieldParsed {
421 field: Field { ident, ty, .. },
422 handling,
423 },
424 )| {
425 let selector = ident.as_ref().map_or_else(
426 || {
427 let i = Literal::usize_unsuffixed(i);
428 quote! {#i}
429 },
430 ToTokens::to_token_stream,
431 );
432 let self_merge_mut_modifier = merge_self_mut.then(|| quote! {&mut});
433 let deref_modifier = (!merge_self_mut).then(|| quote! {*});
434 let self_selector = self_selector_fn(&selector);
435 let other_selector = other_selector_fn(&selector);
436 match (handling, is_self_resolving_optioned(ty)) {
437 (Required, _) | (IsOption, true) => quote! {#deref_modifier #self_selector = #other_selector;},
438 (IsOption, false) => quote! {
439 <#ty as ::optionable::OptionableConvert>::merge(#self_merge_mut_modifier #self_selector, #other_selector)?;
440 },
441 (Other, true) => quote! {
442 if let Some(other_value)=#other_selector{
443 #deref_modifier #self_selector = other_value;
444 }
445 },
446 (Other, false) => quote! {
447 if let Some(other_value)=#other_selector{
448 <#ty as ::optionable::OptionableConvert>::merge(#self_merge_mut_modifier #self_selector, other_value)?;
449 }
450 }
451 }
452 },
453 );
454
455 quote! {
456 #(#fields_token)*
457 }
458}
459
460fn struct_wrapper(
463 tokens: impl Iterator<Item = TokenStream>,
464 struct_name_type: &StructType,
465) -> TokenStream {
466 match struct_name_type {
467 StructType::Named => quote!({#(#tokens),*}),
468 StructType::Unnamed => quote!((#(#tokens),*)),
469 StructType::Unit => quote!(),
470 }
471}
472
473fn patch_where_clause_bounds(
477 where_clause: &mut WhereClause,
478 params: &Punctuated<GenericParam, Token![,]>,
479 predicate: impl Fn(&Type) -> TokenStream,
480) {
481 params.iter().for_each(|param| {
482 if let GenericParam::Type(type_param) = param {
483 let path = &Type::Path(TypePath {
484 qself: None,
485 path: type_param.ident.clone().into(),
486 });
487 add_where_clause_predicate(where_clause, path, &predicate);
488 }
489 });
490}
491
492fn add_where_clause_predicate(
495 where_clause: &mut WhereClause,
496 ty: &Type,
497 entry: impl Fn(&Type) -> TokenStream,
498) {
499 let bounds = where_clause.predicates.iter_mut().find_map(|pred| {
500 if let WherePredicate::Type(pred_ty) = pred
501 && *ty == pred_ty.bounded_ty
502 {
503 Some(&mut pred_ty.bounds)
504 } else {
505 None
506 }
507 });
508 let entry = entry(ty);
509 if let Some(bounds) = bounds {
510 bounds.push(parse_quote!(#entry));
511 } else {
512 where_clause.predicates.push(parse_quote!(#ty: #entry));
513 }
514}
515
516fn error_on_helper_attributes(attrs: &[Attribute], err_msg: &'static str) -> syn::Result<()> {
519 if attrs
520 .iter()
521 .filter(|attr| {
522 println!("{}", attr.path().to_token_stream());
523 attr.path().is_ident(HELPER_IDENT)
524 })
525 .collect::<Vec<_>>()
526 .is_empty()
527 {
528 Ok(())
529 } else {
530 error(err_msg)
531 }
532}
533
534fn into_field_handling(fields: Fields) -> Result<FieldsParsed, Error> {
537 let struct_named = match &fields {
538 Fields::Named(_) => StructType::Named,
539 Fields::Unnamed(_) => StructType::Unnamed,
540 Fields::Unit => StructType::Unit,
541 };
542
543 let fields_iter: Box<dyn Iterator<Item = Field>> = match fields {
544 Fields::Named(f) => Box::new(f.named.into_iter()),
545 Fields::Unnamed(f) => Box::new(f.unnamed.into_iter()),
546 Fields::Unit => Box::new(iter::empty()),
547 };
548 let fields_with_handling = fields_iter
549 .map(|field| {
550 let attrs = FieldHelperAttributes::from_attributes(&field.attrs)?;
551 let handling = if attrs.required.is_some() {
552 Required
553 } else if is_option(&field.ty) {
554 IsOption
555 } else {
556 Other
557 };
558 Ok::<_, Error>(FieldParsed { field, handling })
559 })
560 .collect::<Result<Vec<_>, _>>()?;
561
562 Ok(FieldsParsed {
563 struct_type: struct_named,
564 fields: fields_with_handling,
565 })
566}
567
568#[derive(Debug)]
571enum FieldHandling {
572 Required,
573 IsOption,
574 Other,
575}
576
577#[derive(Debug)]
579struct FieldParsed {
580 field: Field,
581 handling: FieldHandling,
582}
583
584#[derive(Debug, PartialEq)]
586enum StructType {
587 Named,
588 Unnamed,
589 Unit,
590}
591
592#[derive(Debug)]
594struct FieldsParsed {
595 struct_type: StructType,
596 fields: Vec<FieldParsed>,
597}
598
599fn is_option(ty: &Type) -> bool {
601 if let Type::Path(TypePath {
602 qself: _qself,
603 path,
604 }) = &ty
605 && {
606 let segments = &path.segments;
607 (segments.len() == 1 && segments[0].ident == "Option")
608 || (segments.len() == 2
609 && segments[0].ident == "option"
610 && segments[1].ident == "Option")
611 || (segments.len() == 3
612 && segments[0].ident == "std"
613 && segments[1].ident == "option"
614 && segments[2].ident == "Option")
615 }
616 {
617 true
618 } else {
619 false
620 }
621}
622
623fn is_serialize(path: &Path) -> bool {
625 path.is_ident("Serialize") || {
626 let segments = &path.segments;
627 segments.len() == 2 && segments[0].ident == "serde" && segments[1].ident == "Serialize"
628 }
629}
630
631fn optioned_ty(ty: &Type) -> TokenStream {
636 if is_self_resolving_optioned(ty) {
637 ty.to_token_stream()
638 } else {
639 quote! { <#ty as ::optionable::Optionable>::Optioned }
640 }
641}
642
643const SELF_RESOLVING_TYPES: [&str; 17] = [
644 "i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64", "u128", "usize", "f32",
646 "f64", "char", "bool", "String",
648];
649
650fn is_self_resolving_optioned(ty: &Type) -> bool {
653 if let Type::Path(TypePath { qself, path }) = &ty
654 && qself.is_none()
655 && SELF_RESOLVING_TYPES.contains(&&*path.to_token_stream().to_string())
656 {
657 true
658 } else {
659 false
660 }
661}
662
663fn forwarded_attributes(attrs: &[Attribute]) -> Option<TokenStream> {
666 let forward_attrs = attrs
667 .iter()
668 .filter_map(|attr| {
669 if !attr.path().is_ident(HELPER_ATTR_IDENT) {
670 return None;
671 }
672 match &attr.meta {
673 Meta::List(MetaList { tokens, .. }) => Some(quote!(#[#tokens])),
674 _ => None,
675 }
676 })
677 .collect::<TokenStream>();
678 (!forward_attrs.is_empty()).then_some(forward_attrs)
679}
680
681pub(crate) fn error<S: AsRef<str> + fmt::Display, T>(msg: S) -> syn::Result<T> {
683 Err(Error::new(Span::call_site(), msg))
684}
685
686#[cfg(test)]
687mod tests {
688 use crate::derive_optionable;
689 use proc_macro2::TokenStream;
690 use quote::quote;
691
692 struct TestCase {
693 input: TokenStream,
694 output: TokenStream,
695 }
696
697 #[test]
698 #[allow(clippy::too_many_lines)]
699 fn test_optionable() {
700 let tcs = vec![
701 TestCase {
703 input: quote! {
704 #[derive(Optionable)]
705 struct DeriveExample {
706 name: String,
707 pub surname: String,
708 }
709 },
710 output: quote! {
711 #[automatically_derived]
712 struct DeriveExampleOpt {
713 name: Option<String>,
714 pub surname: Option<String>
715 }
716
717 #[automatically_derived]
718 impl ::optionable::Optionable for DeriveExample {
719 type Optioned = DeriveExampleOpt;
720 }
721
722 #[automatically_derived]
723 impl ::optionable::Optionable for DeriveExampleOpt {
724 type Optioned = DeriveExampleOpt;
725 }
726
727 #[automatically_derived]
728 impl ::optionable::OptionableConvert for DeriveExample {
729 fn into_optioned (self) -> DeriveExampleOpt {
730 DeriveExampleOpt {
731 name: Some(self.name),
732 surname:Some(self.surname)
733 }
734 }
735
736 fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::optionable::Error> {
737 Ok(Self{
738 name: value.name.ok_or(optionable::optionable::Error { missing_fields: std::vec!["name"] })?,
739 surname: value.surname.ok_or(optionable::optionable::Error { missing_fields: std::vec!["surname"] })?
740 })
741 }
742
743 fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::optionable::Error> {
744 if let Some(other_value) = other.name {
745 self.name = other_value;
746 }
747 if let Some(other_value) = other.surname {
748 self.surname = other_value;
749 }
750 Ok(())
751 }
752 }
753 },
754 },
755 TestCase {
757 input: quote! {
758 #[derive(Optionable)]
759 #[optionable(no_convert)]
760 struct DeriveExample {
761 name: String,
762 pub surname: String,
763 }
764 },
765 output: quote! {
766 #[automatically_derived]
767 struct DeriveExampleOpt {
768 name: Option<String>,
769 pub surname: Option<String>
770 }
771
772 #[automatically_derived]
773 impl ::optionable::Optionable for DeriveExample {
774 type Optioned = DeriveExampleOpt;
775 }
776
777 #[automatically_derived]
778 impl ::optionable::Optionable for DeriveExampleOpt {
779 type Optioned = DeriveExampleOpt;
780 }
781 },
782 },
783 TestCase {
785 input: quote! {
786 #[derive(Optionable)]
787 struct DeriveExample {
788 name: String,
789 #[optionable(required)]
790 pub surname: String,
791 }
792 },
793 output: quote! {
794 #[automatically_derived]
795 struct DeriveExampleOpt {
796 name: Option<String>,
797 pub surname: String
798 }
799
800 #[automatically_derived]
801 impl ::optionable::Optionable for DeriveExample {
802 type Optioned = DeriveExampleOpt;
803 }
804
805 #[automatically_derived]
806 impl ::optionable::Optionable for DeriveExampleOpt {
807 type Optioned = DeriveExampleOpt;
808 }
809
810 #[automatically_derived]
811 impl ::optionable::OptionableConvert for DeriveExample {
812 fn into_optioned (self) -> DeriveExampleOpt {
813 DeriveExampleOpt {
814 name: Some(self.name),
815 surname: self.surname
816 }
817 }
818
819 fn try_from_optioned(value:DeriveExampleOpt ) -> Result <Self, ::optionable::optionable::Error> {
820 Ok (Self {
821 name: value.name.ok_or(optionable::optionable::Error { missing_fields: std::vec! ["name"] })?,
822 surname: value.surname
823 })
824 }
825
826 fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::optionable::Error> {
827 if let Some(other_value) = other.name {
828 self.name = other_value;
829 }
830 self.surname = other.surname;
831 Ok (())
832 }
833 }
834 },
835 },
836 TestCase {
838 input: quote! {
839 #[derive(Optionable)]
840 #[optionable(derive(Deserialize,Serialize,Default),suffix="Ac")]
841 #[optionable_attr(serde(rename_all = "camelCase", deny_unknown_fields))]
842 #[optionable_attr(serde(default))]
843 struct DeriveExample {
844 #[optionable_attr(serde(rename = "firstName"))]
845 name: String,
846 middle_name: Option<String>,
847 surname: String,
848 }
849 },
850 output: quote! {
851 #[automatically_derived]
852 #[derive(Deserialize, Serialize,Default)]
853 #[serde(rename_all = "camelCase", deny_unknown_fields)]
854 #[serde(default)]
855 struct DeriveExampleAc {
856 #[serde(rename = "firstName")]
857 #[serde(skip_serializing_if = "Option::is_none")]
858 name: Option<String>,
859 #[serde(skip_serializing_if = "Option::is_none")]
860 middle_name: <Option<String> as ::optionable::Optionable>::Optioned,
861 #[serde(skip_serializing_if = "Option::is_none")]
862 surname: Option<String>
863 }
864
865 #[automatically_derived]
866 impl ::optionable::Optionable for DeriveExample {
867 type Optioned = DeriveExampleAc;
868 }
869
870 #[automatically_derived]
871 impl ::optionable::Optionable for DeriveExampleAc {
872 type Optioned = DeriveExampleAc;
873 }
874
875 #[automatically_derived]
876 impl ::optionable::OptionableConvert for DeriveExample {
877 fn into_optioned (self) -> DeriveExampleAc {
878 DeriveExampleAc {
879 name: Some(self.name),
880 middle_name: <Option<String> as ::optionable::OptionableConvert>::into_optioned(self.middle_name),
881 surname: Some(self.surname)
882 }
883 }
884
885 fn try_from_optioned(value: DeriveExampleAc ) -> Result <Self, ::optionable::optionable::Error> {
886 Ok(Self{
887 name: value.name.ok_or(optionable::optionable::Error { missing_fields: std::vec!["name"]})?,
888 middle_name: <Option<String> as ::optionable::OptionableConvert>::try_from_optioned(value.middle_name)?,
889 surname: value.surname.ok_or(optionable::optionable::Error { missing_fields: std::vec!["surname"]})?
890 })
891 }
892
893 fn merge(&mut self, other: DeriveExampleAc ) -> Result<(), ::optionable::optionable::Error> {
894 if let Some(other_value) = other.name {
895 self.name = other_value;
896 }
897 <Option<String> as ::optionable::OptionableConvert>::merge(&mut self.middle_name, other.middle_name)?;
898 if let Some(other_value) = other.surname {
899 self.surname = other_value;
900 }
901 Ok(())
902 }
903 }
904 },
905 },
906 TestCase {
908 input: quote! {
909 #[derive(Optionable)]
910 #[optionable(derive(serde::Deserialize,serde::Serialize),suffix="Ac")]
911 struct DeriveExample {
912 name: String,
913 surname: String,
914 }
915 },
916 output: quote! {
917 #[automatically_derived]
918 #[derive(serde::Deserialize, serde::Serialize)]
919 struct DeriveExampleAc {
920 #[serde(skip_serializing_if = "Option::is_none")]
921 name: Option<String>,
922 #[serde(skip_serializing_if = "Option::is_none")]
923 surname: Option<String>
924 }
925
926 #[automatically_derived]
927 impl ::optionable::Optionable for DeriveExample {
928 type Optioned = DeriveExampleAc;
929 }
930
931 #[automatically_derived]
932 impl ::optionable::Optionable for DeriveExampleAc {
933 type Optioned = DeriveExampleAc;
934 }
935
936 #[automatically_derived]
937 impl ::optionable::OptionableConvert for DeriveExample {
938 fn into_optioned (self) -> DeriveExampleAc {
939 DeriveExampleAc {
940 name: Some(self.name),
941 surname: Some(self.surname)
942 }
943 }
944
945 fn try_from_optioned(value: DeriveExampleAc ) -> Result <Self, ::optionable::optionable::Error> {
946 Ok(Self{
947 name: value.name.ok_or(optionable::optionable::Error{ missing_fields: std::vec!["name"]})?,
948 surname: value.surname.ok_or(optionable::optionable::Error{ missing_fields: std::vec!["surname"]})?
949 })
950 }
951
952 fn merge(&mut self, other: DeriveExampleAc ) -> Result<(), ::optionable::optionable::Error> {
953 if let Some(other_value) = other.name {
954 self.name = other_value;
955 }
956 if let Some(other_value) = other.surname {
957 self.surname = other_value;
958 }
959 Ok(())
960 }
961 }
962 },
963 },
964 TestCase {
966 input: quote! {
967 #[derive(Optionable)]
968 struct DeriveExample(pub String, i32);
969 },
970 output: quote! {
971 #[automatically_derived]
972 struct DeriveExampleOpt(
973 pub Option<String>,
974 Option<i32>
975 );
976
977 #[automatically_derived]
978 impl ::optionable::Optionable for DeriveExample {
979 type Optioned = DeriveExampleOpt;
980 }
981
982 #[automatically_derived]
983 impl ::optionable::Optionable for DeriveExampleOpt {
984 type Optioned = DeriveExampleOpt;
985 }
986
987 #[automatically_derived]
988 impl ::optionable::OptionableConvert for DeriveExample {
989 fn into_optioned (self) -> DeriveExampleOpt {
990 DeriveExampleOpt (
991 Some(self.0),
992 Some(self.1)
993 )
994 }
995
996 fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::optionable::Error> {
997 Ok(Self(
998 value.0.ok_or(optionable::optionable::Error { missing_fields: std::vec!["0"] })?,
999 value.1.ok_or(optionable::optionable::Error { missing_fields: std::vec!["1"] })?
1000 ))
1001 }
1002
1003 fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::optionable::Error> {
1004 if let Some(other_value) = other.0 {
1005 self.0 = other_value;
1006 }
1007 if let Some (other_value) = other.1 {
1008 self.1 = other_value;
1009 }
1010 Ok (())
1011 }
1012 }
1013 },
1014 },
1015 TestCase {
1017 input: quote! {
1018 #[derive(Optionable)]
1019 struct DeriveExample(pub String, #[optionable(required)] i32);
1020 },
1021 output: quote! {
1022 #[automatically_derived]
1023 struct DeriveExampleOpt(
1024 pub Option<String>,
1025 i32
1026 );
1027
1028 #[automatically_derived]
1029 impl ::optionable::Optionable for DeriveExample {
1030 type Optioned = DeriveExampleOpt;
1031 }
1032
1033 #[automatically_derived]
1034 impl ::optionable::Optionable for DeriveExampleOpt {
1035 type Optioned = DeriveExampleOpt;
1036 }
1037
1038 # [automatically_derived]
1039 impl ::optionable::OptionableConvert for DeriveExample {
1040 fn into_optioned (self) -> DeriveExampleOpt {
1041 DeriveExampleOpt (
1042 Some(self.0),
1043 self.1
1044 )
1045 }
1046
1047 fn try_from_optioned(value: DeriveExampleOpt ) -> Result <Self, ::optionable::optionable::Error> {
1048 Ok(Self(
1049 value.0.ok_or(optionable::optionable::Error { missing_fields: std::vec!["0"] })?,
1050 value.1))
1051 }
1052
1053 fn merge(&mut self, other: DeriveExampleOpt ) -> Result<(), ::optionable::optionable::Error> {
1054 if let Some(other_value) = other.0 {
1055 self.0 = other_value;
1056 }
1057 self.1 = other.1;
1058 Ok (())
1059 }
1060 }
1061 },
1062 },
1063 TestCase {
1065 input: quote! {
1066 #[derive(Optionable)]
1067 struct DeriveExample<T, T2: Serialize> where T: DeserializeOwned {
1068 output: T,
1069 input: T2,
1070 }
1071 },
1072 output: quote! {
1073 #[automatically_derived]
1074 struct DeriveExampleOpt<T, T2: Serialize>
1075 where T: DeserializeOwned + ::optionable::Optionable,
1076 T2: ::optionable::Optionable {
1077 output: Option< <T as ::optionable::Optionable>::Optioned>,
1078 input: Option< <T2 as ::optionable::Optionable>::Optioned>
1079 }
1080
1081 #[automatically_derived]
1082 impl<T, T2: Serialize> ::optionable::Optionable for DeriveExample<T, T2>
1083 where T: DeserializeOwned + ::optionable::Optionable,
1084 T2: ::optionable::Optionable {
1085 type Optioned = DeriveExampleOpt<T,T2>;
1086 }
1087
1088 #[automatically_derived]
1089 impl<T, T2: Serialize> ::optionable::Optionable for DeriveExampleOpt<T, T2>
1090 where T: DeserializeOwned + ::optionable::Optionable,
1091 T2: ::optionable::Optionable {
1092 type Optioned = DeriveExampleOpt<T,T2>;
1093 }
1094
1095 #[automatically_derived]
1096 impl <T, T2:Serialize> ::optionable::OptionableConvert for DeriveExample<T, T2 >
1097 where T: DeserializeOwned + ::optionable::OptionableConvert,
1098 T2: ::optionable::OptionableConvert {
1099 fn into_optioned (self) -> DeriveExampleOpt<T, T2> {
1100 DeriveExampleOpt::<T, T2> {
1101 output: Some(<T as::optionable::OptionableConvert>::into_optioned(self.output)),
1102 input: Some(<T2 as::optionable::OptionableConvert>::into_optioned(self.input))
1103 }
1104 }
1105
1106 fn try_from_optioned(value: DeriveExampleOpt<T, T2> ) -> Result <Self, ::optionable::optionable::Error> {
1107 Ok(Self{
1108 output: <T as ::optionable::OptionableConvert>::try_from_optioned(value.output.ok_or(optionable::optionable::Error { missing_fields: std::vec!["output"] })?)?,
1109 input: <T2 as ::optionable::OptionableConvert>::try_from_optioned(value.input.ok_or(optionable::optionable::Error { missing_fields: std::vec!["input"] })?)?
1110 })
1111 }
1112
1113 fn merge(&mut self, other: DeriveExampleOpt<T, T2> ) -> Result<(), ::optionable::optionable::Error> {
1114 if let Some(other_value) = other.output {
1115 <T as ::optionable::OptionableConvert>::merge(&mut self.output, other_value)?;
1116 }
1117 if let Some(other_value) = other.input {
1118 <T2 as ::optionable::OptionableConvert>::merge(&mut self.input, other_value)?;
1119 }
1120 Ok(())
1121 }
1122 }
1123 },
1124 },
1125 TestCase {
1126 input: quote! {
1127 #[derive(Optionable)]
1128 enum DeriveExample {
1129 Unit,
1130 Plain(String),
1131 Address{street: String, number: u32},
1132 Address2(String,u32),
1133 }
1134 },
1135 output: quote! {
1136 # [automatically_derived]
1137 enum DeriveExampleOpt {
1138 Unit,
1139 Plain( Option<String> ),
1140 Address{ street: Option<String>, number:Option<u32> },
1141 Address2( Option<String>, Option<u32> )
1142 }
1143
1144 #[automatically_derived]
1145 impl ::optionable::Optionable for DeriveExample {
1146 type Optioned = DeriveExampleOpt;
1147 }
1148
1149 #[automatically_derived]
1150 impl ::optionable::Optionable for DeriveExampleOpt {
1151 type Optioned = DeriveExampleOpt;
1152 }
1153
1154 #[automatically_derived]
1155 impl ::optionable::OptionableConvert for DeriveExample {
1156 fn into_optioned (self) -> DeriveExampleOpt {
1157 match self{
1158 Self::Unit => DeriveExampleOpt::Unit,
1159 Self::Plain(self_0) => DeriveExampleOpt::Plain(
1160 Some(self_0)
1161 ),
1162 Self::Address{street: self_street, number: self_number} => DeriveExampleOpt::Address{
1163 street: Some(self_street),
1164 number: Some(self_number)
1165 },
1166 Self::Address2(self_0, self_1) => DeriveExampleOpt::Address2(
1167 Some(self_0),
1168 Some(self_1)
1169 )
1170 }
1171 }
1172
1173 fn try_from_optioned(other: DeriveExampleOpt) -> Result <Self, ::optionable::optionable::Error> {
1174 Ok (match other {
1175 DeriveExampleOpt::Unit => Self::Unit,
1176 DeriveExampleOpt::Plain(other_0) => Self::Plain(
1177 other_0.ok_or(optionable::optionable::Error { missing_fields: std::vec!["0"] })?
1178 ),
1179 DeriveExampleOpt::Address{street: other_street, number: other_number} => Self::Address{
1180 street: other_street.ok_or(optionable::optionable::Error { missing_fields: std::vec!["street"] })?,
1181 number: other_number.ok_or(optionable::optionable::Error { missing_fields: std::vec!["number"] })?
1182 },
1183 DeriveExampleOpt::Address2(other_0, other_1) => Self::Address2(
1184 other_0.ok_or(optionable::optionable::Error { missing_fields: std::vec!["0"] })?,
1185 other_1.ok_or(optionable::optionable::Error { missing_fields: std::vec! ["1"] })?)
1186 })
1187 }
1188
1189 fn merge(&mut self, other: DeriveExampleOpt) -> Result<(), ::optionable::optionable::Error> {
1190 match other {
1191 DeriveExampleOpt::Unit => {
1192 if let Self::Unit = self {} else {
1193 *self = Self::try_from_optioned(DeriveExampleOpt::Unit)?;
1194 }
1195 },
1196 DeriveExampleOpt::Plain(other_0) => {
1197 if let Self::Plain(self_0) = self{
1198 if let Some(other_value) = other_0 {
1199 *self_0 = other_value;
1200 }
1201 } else {
1202 *self = Self::try_from_optioned(DeriveExampleOpt::Plain(other_0))?;
1203 }
1204 },
1205 DeriveExampleOpt::Address{street: other_street, number: other_number} => {
1206 if let Self::Address{street: self_street, number: self_number} = self{
1207 if let Some(other_value) = other_street {
1208 *self_street = other_value;
1209 }
1210 if let Some(other_value) = other_number {
1211 *self_number = other_value;
1212 }
1213 } else {
1214 *self = Self::try_from_optioned(DeriveExampleOpt::Address{street: other_street, number: other_number})?;
1215 }
1216 },
1217 DeriveExampleOpt::Address2(other_0, other_1) => {
1218 if let Self::Address2(self_0, self_1) = self{
1219 if let Some(other_value) = other_0 {
1220 *self_0 = other_value;
1221 }
1222 if let Some(other_value) = other_1 {
1223 *self_1 = other_value;
1224 }
1225 } else {
1226 *self = Self::try_from_optioned(DeriveExampleOpt::Address2(other_0, other_1))?;
1227 }
1228 }
1229 }
1230 Ok(())
1231 }
1232 }
1233 },
1234 },
1235 ];
1236 for tc in tcs {
1237 let input = syn::parse2(tc.input).unwrap();
1238 let output = derive_optionable(input).unwrap();
1239 println!("{output}");
1240 assert_eq!(tc.output.to_string(), output.to_string());
1241 }
1242 }
1243}