Skip to content

Commit

Permalink
Merge pull request #122 from aumetra/angle-bracket-path
Browse files Browse the repository at this point in the history
Add support for paths with angle brackets
  • Loading branch information
idanarye authored Oct 15, 2023
2 parents cdbff99 + b179798 commit 370fdd7
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 49 deletions.
12 changes: 12 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Foo>))]
struct Foo {
value: i32,
}

let foo: std::sync::Arc<Foo> = Foo::builder().value(42).build();
assert_eq!(*foo, Foo { value: 42 });
}

#[test]
fn test_into_set_generic_impl_into() {
#[derive(TypedBuilder)]
Expand Down
38 changes: 16 additions & 22 deletions typed-builder-macro/src/field_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 { .. } => {
Expand All @@ -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::<syn::LitStr>()?;
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),
Expand All @@ -258,24 +252,24 @@ 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;
}
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<KeyValue> = 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(())
Expand All @@ -293,28 +287,28 @@ 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
};
Ok(())
}
"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
};
Ok(())
}
"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
};
Expand Down
34 changes: 11 additions & 23 deletions typed-builder-macro/src/struct_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,26 +631,21 @@ pub struct CommonDeclarationSettings {
pub name: Option<syn::Expr>,
pub doc: Option<syn::Expr>,
}

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::<syn::LitStr>()?.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(
Expand Down Expand Up @@ -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 {
Expand All @@ -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::<syn::TypePath>()?;
self.into = IntoSetting::TypeConversionToSpecificType(type_path);
Ok(())
}
_ => Err(expr.incorrect_type()),
Expand Down Expand Up @@ -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::<syn::ExprPath>()?;
self.crate_module_path = crate_module_path.path;
Ok(())
}
"builder_method_doc" => Err(Error::new_spanned(
expr.name(),
Expand Down
28 changes: 24 additions & 4 deletions typed-builder-macro/src/util.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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<T: Parse>(self) -> syn::Result<T> {
syn::parse2(self.value)
}
}

impl ToTokens for KeyValue {
Expand Down Expand Up @@ -238,6 +244,20 @@ impl ToTokens for SubAttr {
}
}

fn take_until_comma(input: ParseStream) -> syn::Result<TokenStream> {
let mut stream = TokenStream::new();
while !input.is_empty() {
if input.peek(Token![,]) {
break;
}

let token = input.parse::<TokenTree>()?;
stream.extend(Some(token));
}

Ok(stream)
}

impl Parse for AttrArg {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
if input.peek(Token![!]) {
Expand All @@ -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 !<ident>, <ident>=<value> or <ident>(…)"))
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 370fdd7

Please sign in to comment.