Skip to content

Commit

Permalink
Into support for simple and optional fields
Browse files Browse the repository at this point in the history
Fixes #24
  • Loading branch information
BrynCooke committed Apr 26, 2022
1 parent 6f03d7d commit 1bb0113
Show file tree
Hide file tree
Showing 18 changed files with 125 additions and 80 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.1.9 - unreleased
[#5](https://github.com/BrynCooke/buildstructor/issues/24)
Simple types are now given Into treatment globally.

## 0.1.8 - 2022-04-25
[#5](https://github.com/BrynCooke/buildstructor/issues/5)
Simple types are now given Into treatment when inserting to collection via singular form.
Expand Down
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,16 @@ fn main() {

### Into field

You can use generics as usual in your constructor.
#### Simple types
Types automatically have into conversion if:
* the type is not a scalar.
* the type has no generic parameters. (this may be relaxed later)
* the type is a generic parameter from the impl or constructor method.

This is useful for Strings, but also other types where you want to overload the singular build method. Create an enum that derives From for all the types you want to support and then use this type in your constructor.

#### Complex types
You can use generics as usual in your constructor. However, this has the downside of not being able to support optional fields.

```rust
use buildstructor::builder;
Expand Down Expand Up @@ -252,7 +261,7 @@ In the case that a singular form cannot be derived automatically the suffix `_en

Adding a singular entry will automatically perform an into conversion if:
* the type is not a scalar.
* the type has no generic paramaters. (this may be relaxed later)
* the type has no generic parameters. (this may be relaxed later)
* the type is a generic parameter from the impl or constructor method.

This is useful for Strings, but also other types where you want to overload the singular build method. Create an enum that derives From for all the types you want to support and then use this type in your constructor.
Expand Down
48 changes: 31 additions & 17 deletions src/buildstructor/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,18 +165,15 @@ pub fn builder_methods(

let set = call(format_ident!("__set"), vec![Expr::Path(field_name.to_expr_path())]);
let new_state = params(ir, idx, field_name, &builder_type_generics, set);
let set_some = call(format_ident!("__set"), vec![call(format_ident!("Some"), vec![Expr::Path(field_name.to_expr_path())])]);

let builder_type_generics = Generics::combine(vec![&builder_type_generics.without(idx), &builder_generics]);

match f.field_type {
FieldType::Option => {
let and_method_name = format_ident!("and_{}", f.name);
let new_state_option = params(ir, idx, field_name, &builder_type_generics, set_some);
let mut field_collection_type = f.collection_type.clone();
let mut field_collection_type = f.generic_type.clone();
let mut into_generics = None;
let mut into_call = None;
if f.collection_into {
if f.generic_into {
let into_type = field_collection_type.replace(Type::parse("__T"));
let _ = into_generics.insert(Some(quote! {
<__T: Into<#into_type>>
Expand All @@ -188,12 +185,14 @@ pub fn builder_methods(
quote! {
impl #builder_type_generics #builder_name #before {
pub fn #method_name #into_generics(self, #field_name: #field_collection_type) -> #builder_name #after #builder_where_clause {
let #field_name = Some(#field_name #into_call);
#builder_name {
fields: #new_state_option,
fields: #new_state,
_phantom: core::default::Default::default()
}
}
pub fn #and_method_name #into_generics(self, #field_name: #ty) -> #builder_name #after #builder_where_clause {
pub fn #and_method_name #into_generics(self, #field_name: Option<#field_collection_type>) -> #builder_name #after #builder_where_clause {
let #field_name = #field_name.map(|v|v #into_call);
#builder_name {
fields: #new_state,
_phantom: core::default::Default::default()
Expand All @@ -204,10 +203,10 @@ pub fn builder_methods(
},
FieldType::Set => {
let (singular, plural) = single_plural_names(field_name);
let mut field_collection_type = f.collection_type.clone();
let mut field_collection_type = f.generic_type.clone();
let mut into_generics = None;
let mut into_call = None;
if f.collection_into {
if f.generic_into {
let into_type = field_collection_type.replace(Type::parse("__T"));
let _ = into_generics.insert(Some(quote! {
<__T: Into<#into_type>>
Expand Down Expand Up @@ -235,10 +234,10 @@ pub fn builder_methods(
},
FieldType::Vec => {
let (singular, plural) = single_plural_names(field_name);
let mut field_collection_type = f.collection_type.clone();
let mut field_collection_type = f.generic_type.clone();
let mut into_generics = None;
let mut into_call = None;
if f.collection_into {
if f.generic_into {
let into_type = field_collection_type.replace(Type::parse("__T"));
let _ = into_generics.insert(Some(quote! {
<__T: Into<#into_type>>
Expand Down Expand Up @@ -317,12 +316,27 @@ pub fn builder_methods(
}
}
},
_ => quote! {
impl #builder_type_generics #builder_name #before {
pub fn #method_name (self, #field_name: #ty) -> #builder_name #after {
#builder_name {
fields: #new_state,
_phantom: core::default::Default::default()
_ => {
let mut into_generics = None;
let mut into_call = None;
let mut ty = Some(ty.clone());
if f.into {
let into_type = ty.replace(Type::parse("__T"));
let _ = into_generics.insert(Some(quote! {
<__T: Into<#into_type>>
}));
into_call = Some(quote!{
.into()
})
}
quote! {
impl #builder_type_generics #builder_name #before {
pub fn #method_name #into_generics(self, #field_name: #ty) -> #builder_name #after {
let #field_name = #field_name #into_call;
#builder_name {
fields: #new_state,
_phantom: core::default::Default::default()
}
}
}
}
Expand Down
79 changes: 40 additions & 39 deletions src/buildstructor/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ pub struct Ir {
pub struct BuilderField {
pub name: Ident,
pub ty: Type,
pub into: bool,
pub field_type: FieldType,
pub key_type: Option<Type>,
pub key_into: bool,
pub value_type: Option<Type>,
pub value_into: bool,
pub collection_type: Option<Type>,
pub collection_into: bool,
pub generic_type: Option<Type>,
pub generic_into: bool,
}

#[derive(Debug)]
Expand Down Expand Up @@ -88,56 +89,56 @@ fn builder_fields(model: &ConstrutorModel) -> Vec<BuilderField> {
let ident = try_match!(&*t.pat, Pat::Ident(x)=>x).ok()?;
let field_type = field_type(&*t.ty);
let args = t.ty.generic_args();
let (
(key_type, key_into),
(value_type, value_into),
(collection_type, collection_into),
) = match (
&field_type,
args.and_then(|args| args.iter().next()),
args.and_then(|args| args.iter().nth(1)),
) {
(
FieldType::Option | FieldType::Vec | FieldType::Set,
Some(GenericArgument::Type(collection_type)),
None,
) => (
(None, false),
(None, false),
let ((key_type, key_into), (value_type, value_into), (generic_type, generic_into)) =
match (
&field_type,
args.and_then(|args| args.iter().next()),
args.and_then(|args| args.iter().nth(1)),
) {
(
Some(collection_type.clone()),
collection_type
.is_into_capable(&model.generics, &model.method_generics),
),
),
(
FieldType::Map,
Some(GenericArgument::Type(key_type)),
Some(GenericArgument::Type(value_type)),
) => (
(
Some(key_type.clone()),
key_type.is_into_capable(&model.generics, &model.method_generics),
FieldType::Option | FieldType::Vec | FieldType::Set,
Some(GenericArgument::Type(collection_type)),
None,
) => (
(None, false),
(None, false),
(
Some(collection_type.clone()),
collection_type
.is_into_capable(&model.generics, &model.method_generics),
),
),
(
Some(value_type.clone()),
value_type.is_into_capable(&model.generics, &model.method_generics),
FieldType::Map,
Some(GenericArgument::Type(key_type)),
Some(GenericArgument::Type(value_type)),
) => (
(
Some(key_type.clone()),
key_type.is_into_capable(&model.generics, &model.method_generics),
),
(
Some(value_type.clone()),
value_type.is_into_capable(&model.generics, &model.method_generics),
),
(None, false),
),
(None, false),
),
_ => ((None, false), (None, false), (None, false)),
};
_ => ((None, false), (None, false), (None, false)),
};

let into =
t.ty.is_into_capable(&model.generics, &model.method_generics);
Some(BuilderField {
ty: *t.ty.clone(),
into,
name: ident.ident.clone(),
field_type,
key_type,
key_into,
value_type,
value_into,
collection_type,
collection_into,
generic_type,
generic_into,
})
}
FnArg::Receiver(_) => None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ mod __foo_new_builder {
}
impl __FooBuilder<(__Required<usize>,)> {
pub fn simple(self, simple: usize) -> __FooBuilder<(__Set<usize>,)> {
let simple = simple;
__FooBuilder {
fields: (__set(simple),),
_phantom: core::default::Default::default(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ mod __foo_new_builder {
self,
simple: usize,
) -> __FooBuilder<(__Set<usize>, __1, __2, __3, __4, __5)> {
let simple = simple;
__FooBuilder {
fields: (
__set(simple),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ mod __foo_new_builder {
}
impl __FooBuilder<(__Required<usize>,)> {
pub fn simple(self, simple: usize) -> __FooBuilder<(__Set<usize>,)> {
let simple = simple;
__FooBuilder {
fields: (__set(simple),),
_phantom: core::default::Default::default(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ mod __foo_new_builder {
}
impl<T> __FooBuilder<(__Required<T>,), T> {
pub fn simple(self, simple: T) -> __FooBuilder<(__Set<T>,), T> {
let simple = simple;
__FooBuilder {
fields: (__set(simple),),
_phantom: core::default::Default::default(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ mod __foo_new_builder {
}
impl<T: Into<String>> __FooBuilder<(__Required<T>,), T> {
pub fn simple(self, simple: T) -> __FooBuilder<(__Set<T>,), T> {
let simple = simple;
__FooBuilder {
fields: (__set(simple),),
_phantom: core::default::Default::default(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ mod __foo_new_builder {
}
impl<T> __FooBuilder<(__Required<T>,), T> {
pub fn simple(self, simple: T) -> __FooBuilder<(__Set<T>,), T> {
let simple = simple;
__FooBuilder {
fields: (__set(simple),),
_phantom: core::default::Default::default(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ mod __foo_new_builder {
}
impl<__1> __FooBuilder<(__Required<usize>, __1)> {
pub fn simple(self, simple: usize) -> __FooBuilder<(__Set<usize>, __1)> {
let simple = simple;
__FooBuilder {
fields: (__set(simple), self.fields.1),
_phantom: core::default::Default::default(),
Expand All @@ -60,6 +61,7 @@ mod __foo_new_builder {
}
impl<__0> __FooBuilder<(__0, __Required<usize>)> {
pub fn simple2(self, simple2: usize) -> __FooBuilder<(__0, __Set<usize>)> {
let simple2 = simple2;
__FooBuilder {
fields: (self.fields.0, __set(simple2)),
_phantom: core::default::Default::default(),
Expand Down
Loading

0 comments on commit 1bb0113

Please sign in to comment.