diff --git a/tests/tests.rs b/tests/tests.rs index 859a08fb..db9761c0 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -719,6 +719,18 @@ fn test_into_set_generic_impl_from() { assert_eq!(bar, Bar { value: 42 }); } +#[test] +fn test_into_angle_bracket_type() { + #[derive(Debug, PartialEq, TypedBuilder)] + #[builder(build_method(into = std::sync::Arc))] + struct Foo { + value: i32, + } + + let foo: std::sync::Arc = Foo::builder().value(42).build(); + assert_eq!(*foo, Foo { value: 42 }); +} + #[test] fn test_into_set_generic_impl_into() { #[derive(TypedBuilder)] diff --git a/typed-builder-macro/src/field_info.rs b/typed-builder-macro/src/field_info.rs index ac5fae19..e13a2286 100644 --- a/typed-builder-macro/src/field_info.rs +++ b/typed-builder-macro/src/field_info.rs @@ -222,7 +222,7 @@ impl ApplyMeta for FieldBuilderAttr<'_> { Ok(()) } AttrArg::KeyValue(key_value) => { - self.default = Some(key_value.value); + self.default = Some(key_value.parse_value()?); Ok(()) } AttrArg::Not { .. } => { @@ -232,18 +232,12 @@ impl ApplyMeta for FieldBuilderAttr<'_> { AttrArg::Sub(_) => Err(expr.incorrect_type()), }, "default_code" => { - let value = expr.key_value()?.value; - if let syn::Expr::Lit(syn::ExprLit { - lit: syn::Lit::Str(code), - .. - }) = value - { - use std::str::FromStr; - let tokenized_code = TokenStream::from_str(&code.value())?; - self.default = Some(syn::parse2(tokenized_code).map_err(|e| Error::new_spanned(code, format!("{}", e)))?); - } else { - return Err(Error::new_spanned(value, "Expected string")); - } + use std::str::FromStr; + + let code = expr.key_value()?.parse_value::()?; + let tokenized_code = TokenStream::from_str(&code.value())?; + self.default = Some(syn::parse2(tokenized_code).map_err(|e| Error::new_spanned(code, format!("{}", e)))?); + Ok(()) } "setter" => self.setter.apply_sub_attr(expr), @@ -258,7 +252,7 @@ impl ApplyMeta for FieldBuilderAttr<'_> { Some(syn::parse2(quote_spanned!(ident.span() => ::core::default::Default::default())).unwrap()); } AttrArg::KeyValue(key_value) => { - self.via_mutators = Some(key_value.value); + self.via_mutators = Some(key_value.parse_value()?); } AttrArg::Not { .. } => { self.via_mutators = None; @@ -266,16 +260,16 @@ impl ApplyMeta for FieldBuilderAttr<'_> { AttrArg::Sub(sub) => { let paren_span = sub.paren.span.span(); let mut args = sub.args()?.into_iter(); - let Some(KeyValue { name, value, .. }) = args.next() else { + let Some(key_value): Option = args.next() else { return Err(Error::new(paren_span, "Expected `init = ...`")); }; - if name != "init" { - return Err(Error::new_spanned(name, "Expected `init`")); + if key_value.name != "init" { + return Err(Error::new_spanned(key_value.name, "Expected `init`")); } if let Some(remaining) = args.next() { return Err(Error::new_spanned(remaining, "Expected only one argument (`init = ...`)")); } - self.via_mutators = Some(value); + self.via_mutators = Some(key_value.parse_value()?); } } Ok(()) @@ -293,12 +287,12 @@ impl ApplyMeta for SetterSettings { fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> { match expr.name().to_string().as_str() { "doc" => { - self.doc = expr.key_value_or_not()?.map(|kv| kv.value); + self.doc = expr.key_value_or_not()?.map(|kv| kv.parse_value()).transpose()?; Ok(()) } "transform" => { self.transform = if let Some(key_value) = expr.key_value_or_not()? { - Some(parse_transform_closure(key_value.name.span(), key_value.value)?) + Some(parse_transform_closure(key_value.name.span(), key_value.parse_value()?)?) } else { None }; @@ -306,7 +300,7 @@ impl ApplyMeta for SetterSettings { } "prefix" => { self.prefix = if let Some(key_value) = expr.key_value_or_not()? { - Some(expr_to_lit_string(&key_value.value)?) + Some(expr_to_lit_string(&key_value.parse_value()?)?) } else { None }; @@ -314,7 +308,7 @@ impl ApplyMeta for SetterSettings { } "suffix" => { self.suffix = if let Some(key_value) = expr.key_value_or_not()? { - Some(expr_to_lit_string(&key_value.value)?) + Some(expr_to_lit_string(&key_value.parse_value()?)?) } else { None }; diff --git a/typed-builder-macro/src/struct_info.rs b/typed-builder-macro/src/struct_info.rs index d0cc1cb4..65497e9c 100644 --- a/typed-builder-macro/src/struct_info.rs +++ b/typed-builder-macro/src/struct_info.rs @@ -631,26 +631,21 @@ pub struct CommonDeclarationSettings { pub name: Option, pub doc: Option, } + impl ApplyMeta for CommonDeclarationSettings { fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> { match expr.name().to_string().as_str() { "vis" => { - let value = expr.key_value()?.value; - let syn::Expr::Lit(expr_lit) = value else { - return Err(Error::new_spanned(value, "invalid visibility found")); - }; - let syn::Lit::Str(expr_str) = expr_lit.lit else { - return Err(Error::new_spanned(expr_lit, "invalid visibility found")); - }; - self.vis = Some(syn::parse_str(&expr_str.value())?); + let expr_str = expr.key_value()?.parse_value::()?.value(); + self.vis = Some(syn::parse_str(&expr_str)?); Ok(()) } "name" => { - self.name = Some(expr.key_value()?.value); + self.name = Some(expr.key_value()?.parse_value()?); Ok(()) } "doc" => { - self.doc = Some(expr.key_value()?.value); + self.doc = Some(expr.key_value()?.parse_value()?); Ok(()) } _ => Err(Error::new_spanned( @@ -684,7 +679,7 @@ pub enum IntoSetting { /// Convert the build value into the generic parameter passed to the `build` method. GenericConversion, /// Convert the build value into a specific type specified in the attribute. - TypeConversionToSpecificType(syn::ExprPath), + TypeConversionToSpecificType(syn::TypePath), } impl Default for IntoSetting { @@ -710,11 +705,8 @@ impl ApplyMeta for BuildMethodSettings { Ok(()) } AttrArg::KeyValue(key_value) => { - let expr_path = match key_value.value { - syn::Expr::Path(expr_path) => expr_path, - _ => return Err(Error::new_spanned(&key_value.value, "Expected path expression type")), - }; - self.into = IntoSetting::TypeConversionToSpecificType(expr_path.clone()); + let type_path = key_value.parse_value::()?; + self.into = IntoSetting::TypeConversionToSpecificType(type_path); Ok(()) } _ => Err(expr.incorrect_type()), @@ -791,13 +783,9 @@ impl ApplyMeta for TypeBuilderAttr<'_> { fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error> { match expr.name().to_string().as_str() { "crate_module_path" => { - let value = expr.key_value()?.value; - if let syn::Expr::Path(crate_module_path) = value { - self.crate_module_path = crate_module_path.path.clone(); - Ok(()) - } else { - Err(Error::new_spanned(value, "crate_module_path must be a path")) - } + let crate_module_path = expr.key_value()?.parse_value::()?; + self.crate_module_path = crate_module_path.path; + Ok(()) } "builder_method_doc" => Err(Error::new_spanned( expr.name(), diff --git a/typed-builder-macro/src/util.rs b/typed-builder-macro/src/util.rs index d8ab7cf2..6687351d 100644 --- a/typed-builder-macro/src/util.rs +++ b/typed-builder-macro/src/util.rs @@ -1,6 +1,6 @@ use std::{collections::HashSet, iter}; -use proc_macro2::{Ident, Span, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream, TokenTree}; use quote::{format_ident, ToTokens}; use syn::{ parenthesized, @@ -195,7 +195,13 @@ impl AttrArg { pub struct KeyValue { pub name: Ident, pub eq: Token![=], - pub value: Expr, + pub value: TokenStream, +} + +impl KeyValue { + pub fn parse_value(self) -> syn::Result { + syn::parse2(self.value) + } } impl ToTokens for KeyValue { @@ -238,6 +244,20 @@ impl ToTokens for SubAttr { } } +fn take_until_comma(input: ParseStream) -> syn::Result { + let mut stream = TokenStream::new(); + while !input.is_empty() { + if input.peek(Token![,]) { + break; + } + + let token = input.parse::()?; + stream.extend(Some(token)); + } + + Ok(stream) +} + impl Parse for AttrArg { fn parse(input: syn::parse::ParseStream) -> syn::Result { if input.peek(Token![!]) { @@ -260,7 +280,7 @@ impl Parse for AttrArg { Ok(Self::KeyValue(KeyValue { name, eq: input.parse()?, - value: input.parse()?, + value: take_until_comma(input)?, })) } else { Err(input.error("expected !, = or (…)")) @@ -332,7 +352,7 @@ impl ApplyMeta for MutatorAttribute { return Err(Error::new_spanned(expr.name(), "Only `requires` is supported")); } - match expr.key_value()?.value { + match expr.key_value()?.parse_value()? { Expr::Array(syn::ExprArray { elems, .. }) => self.requires.extend( elems .into_iter()