|
@@ -43,27 +43,21 @@ pub fn impl_my_derive(ast: &syn::DeriveInput) -> Result<TokenStream, Error> {
|
|
syn::Fields::Unnamed(_) => {
|
|
syn::Fields::Unnamed(_) => {
|
|
return Err(Error::new(
|
|
return Err(Error::new(
|
|
ast.span(),
|
|
ast.span(),
|
|
- "TypedBuilder is not supported for tuple structs",
|
|
|
|
|
|
+ "Props is not supported for tuple structs",
|
|
))
|
|
))
|
|
}
|
|
}
|
|
syn::Fields::Unit => {
|
|
syn::Fields::Unit => {
|
|
return Err(Error::new(
|
|
return Err(Error::new(
|
|
ast.span(),
|
|
ast.span(),
|
|
- "TypedBuilder is not supported for unit structs",
|
|
|
|
|
|
+ "Props is not supported for unit structs",
|
|
))
|
|
))
|
|
}
|
|
}
|
|
},
|
|
},
|
|
syn::Data::Enum(_) => {
|
|
syn::Data::Enum(_) => {
|
|
- return Err(Error::new(
|
|
|
|
- ast.span(),
|
|
|
|
- "TypedBuilder is not supported for enums",
|
|
|
|
- ))
|
|
|
|
|
|
+ return Err(Error::new(ast.span(), "Props is not supported for enums"))
|
|
}
|
|
}
|
|
syn::Data::Union(_) => {
|
|
syn::Data::Union(_) => {
|
|
- return Err(Error::new(
|
|
|
|
- ast.span(),
|
|
|
|
- "TypedBuilder is not supported for unions",
|
|
|
|
- ))
|
|
|
|
|
|
+ return Err(Error::new(ast.span(), "Props is not supported for unions"))
|
|
}
|
|
}
|
|
};
|
|
};
|
|
Ok(data)
|
|
Ok(data)
|
|
@@ -169,6 +163,7 @@ mod util {
|
|
}
|
|
}
|
|
|
|
|
|
mod field_info {
|
|
mod field_info {
|
|
|
|
+ use crate::props::type_from_inside_option;
|
|
use proc_macro2::TokenStream;
|
|
use proc_macro2::TokenStream;
|
|
use quote::quote;
|
|
use quote::quote;
|
|
use syn::parse::Error;
|
|
use syn::parse::Error;
|
|
@@ -202,6 +197,16 @@ mod field_info {
|
|
Some(syn::parse(quote!(Default::default()).into()).unwrap());
|
|
Some(syn::parse(quote!(Default::default()).into()).unwrap());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // auto detect optional
|
|
|
|
+ let strip_option_auto = builder_attr.strip_option
|
|
|
|
+ || !builder_attr.ignore_option
|
|
|
|
+ && type_from_inside_option(&field.ty, true).is_some();
|
|
|
|
+ if !builder_attr.strip_option && strip_option_auto {
|
|
|
|
+ builder_attr.strip_option = true;
|
|
|
|
+ builder_attr.default =
|
|
|
|
+ Some(syn::parse(quote!(Default::default()).into()).unwrap());
|
|
|
|
+ }
|
|
|
|
+
|
|
Ok(FieldInfo {
|
|
Ok(FieldInfo {
|
|
ordinal,
|
|
ordinal,
|
|
name,
|
|
name,
|
|
@@ -236,31 +241,8 @@ mod field_info {
|
|
.into()
|
|
.into()
|
|
}
|
|
}
|
|
|
|
|
|
- pub fn type_from_inside_option(&self) -> Option<&syn::Type> {
|
|
|
|
- let path = if let syn::Type::Path(type_path) = self.ty {
|
|
|
|
- if type_path.qself.is_some() {
|
|
|
|
- return None;
|
|
|
|
- } else {
|
|
|
|
- &type_path.path
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- return None;
|
|
|
|
- };
|
|
|
|
- let segment = path.segments.last()?;
|
|
|
|
- if segment.ident != "Option" {
|
|
|
|
- return None;
|
|
|
|
- }
|
|
|
|
- let generic_params =
|
|
|
|
- if let syn::PathArguments::AngleBracketed(generic_params) = &segment.arguments {
|
|
|
|
- generic_params
|
|
|
|
- } else {
|
|
|
|
- return None;
|
|
|
|
- };
|
|
|
|
- if let syn::GenericArgument::Type(ty) = generic_params.args.first()? {
|
|
|
|
- Some(ty)
|
|
|
|
- } else {
|
|
|
|
- None
|
|
|
|
- }
|
|
|
|
|
|
+ pub fn type_from_inside_option(&self, check_option_name: bool) -> Option<&syn::Type> {
|
|
|
|
+ type_from_inside_option(self.ty, check_option_name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -271,6 +253,7 @@ mod field_info {
|
|
pub skip: bool,
|
|
pub skip: bool,
|
|
pub auto_into: bool,
|
|
pub auto_into: bool,
|
|
pub strip_option: bool,
|
|
pub strip_option: bool,
|
|
|
|
+ pub ignore_option: bool,
|
|
}
|
|
}
|
|
|
|
|
|
impl FieldBuilderAttr {
|
|
impl FieldBuilderAttr {
|
|
@@ -427,8 +410,9 @@ mod field_info {
|
|
self.auto_into = false;
|
|
self.auto_into = false;
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
- "strip_option" => {
|
|
|
|
|
|
+ "optional" => {
|
|
self.strip_option = false;
|
|
self.strip_option = false;
|
|
|
|
+ self.ignore_option = true;
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
_ => Err(Error::new_spanned(path, "Unknown setting".to_owned())),
|
|
_ => Err(Error::new_spanned(path, "Unknown setting".to_owned())),
|
|
@@ -446,6 +430,33 @@ mod field_info {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+fn type_from_inside_option(ty: &syn::Type, check_option_name: bool) -> Option<&syn::Type> {
|
|
|
|
+ let path = if let syn::Type::Path(type_path) = ty {
|
|
|
|
+ if type_path.qself.is_some() {
|
|
|
|
+ return None;
|
|
|
|
+ } else {
|
|
|
|
+ &type_path.path
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ return None;
|
|
|
|
+ };
|
|
|
|
+ let segment = path.segments.last()?;
|
|
|
|
+ if check_option_name && segment.ident != "Option" {
|
|
|
|
+ return None;
|
|
|
|
+ }
|
|
|
|
+ let generic_params =
|
|
|
|
+ if let syn::PathArguments::AngleBracketed(generic_params) = &segment.arguments {
|
|
|
|
+ generic_params
|
|
|
|
+ } else {
|
|
|
|
+ return None;
|
|
|
|
+ };
|
|
|
|
+ if let syn::GenericArgument::Type(ty) = generic_params.args.first()? {
|
|
|
|
+ Some(ty)
|
|
|
|
+ } else {
|
|
|
|
+ None
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
mod struct_info {
|
|
mod struct_info {
|
|
use proc_macro2::TokenStream;
|
|
use proc_macro2::TokenStream;
|
|
use quote::quote;
|
|
use quote::quote;
|
|
@@ -766,7 +777,7 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|
// NOTE: both auto_into and strip_option affect `arg_type` and `arg_expr`, but the order of
|
|
// NOTE: both auto_into and strip_option affect `arg_type` and `arg_expr`, but the order of
|
|
// nesting is different so we have to do this little dance.
|
|
// nesting is different so we have to do this little dance.
|
|
let arg_type = if field.builder_attr.strip_option {
|
|
let arg_type = if field.builder_attr.strip_option {
|
|
- let internal_type = field.type_from_inside_option().ok_or_else(|| {
|
|
|
|
|
|
+ let internal_type = field.type_from_inside_option(false).ok_or_else(|| {
|
|
Error::new_spanned(
|
|
Error::new_spanned(
|
|
&field_type,
|
|
&field_type,
|
|
"can't `strip_option` - field is not `Option<...>`",
|
|
"can't `strip_option` - field is not `Option<...>`",
|