Skip to content

Commit

Permalink
Merge pull request #14 from ImaMapleTree/release/1.0.5-add-named-ctor…
Browse files Browse the repository at this point in the history
…-properties

Added new named ctor properties
  • Loading branch information
ImaMapleTree authored Jun 9, 2024
2 parents 2741887 + c448485 commit 43b0b7e
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "derive-ctor"
version = "1.0.4"
version = "1.0.5"
description = "Adds `#[derive(ctor)]` which allows for the auto-generation of struct, enum, and union constructors."
keywords = ["derive", "macro", "trait", "procedural", "no_std"]
authors = ["Evan Cowin"]
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Add `derive-ctor` to your `Cargo.toml`:

```toml
[dependencies]
derive-ctor = "1.0.4"
derive-ctor = "1.0.5"
```

Annotate your struct with `#[derive(ctor)]` to automatically generate a `new` constructor:
Expand Down Expand Up @@ -97,6 +97,26 @@ struct OtherStruct {
let default2: OtherStruct = Default::default();
```

### Constructor Properties
Custom constructor definitions can also take one of the following properties to implement on all non-configured fields
- **default** - Marks all non-annotated fields as `#[ctor(default)]`
- **into** - Marks all non-annotated fields as `#[ctor(into)]`

```rust
use derive_ctor::ctor;

#[derive(ctor)]
#[ctor(new(into), with_defaults(default))]
struct MyStruct {
name: String,
#[ctor(expr(13))]
value: i32
}

let example1 = MyStruct::new("FooBar");
let example2 = MyStruct::with_defaults();
```

## Enum and Union Configurations

By default, a constructor will be generated for each variant. This constructor by default will match the name of its
Expand Down
1 change: 1 addition & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub(crate) const ENUM_PROP_VIS: &str = "vis";
pub(crate) const ENUM_VARIATION_PROP_NONE: &str = "none";

// struct config properties
pub(crate) const STRUCT_PROP_INTO: &str = "into";
pub(crate) const STRUCT_PROP_DEFAULT: &str = "default";
// property used within the default() prop
pub(crate) const NESTED_PROP_ALL: &str = "all";
11 changes: 8 additions & 3 deletions src/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,16 @@ pub(crate) fn generate_ctor_meta(
let mut req_field_type = None;
let mut gen_configuration = None;
let is_default_all = ctor_attributes.contains(&CtorAttribute::DefaultAll);
let is_into_all = ctor_attributes.contains(&CtorAttribute::IntoAll);

match &configuration {
None if is_default_all => {
gen_configuration = Some(FieldConfigProperty::Default)
}
None if is_into_all => {
req_field_type = Some(parse2(quote! { impl Into<#ft> }).expect("Could not parse `Into` type"));
gen_configuration = Some(FieldConfigProperty::Into)
}
None if is_phantom_data(&field.ty) => {
gen_configuration = Some(FieldConfigProperty::Default)
}
Expand All @@ -250,12 +255,12 @@ pub(crate) fn generate_ctor_meta(
if applications.is_empty() || applications.contains(&ctor_index) {
// create a required field type if the configuration requires an additional input parameter
req_field_type = match &configuration.property {
FieldConfigProperty::Cloned => Some(parse2(quote! { &#ft }).unwrap()),
FieldConfigProperty::Cloned => Some(parse2(quote! { &#ft }).expect("Could not parse ref type")),
FieldConfigProperty::Into => {
Some(parse2(quote! { impl Into<#ft> }).unwrap())
Some(parse2(quote! { impl Into<#ft> }).expect("Could not parse `Into` type"))
}
FieldConfigProperty::Iter { iter_type } => {
Some(parse2(quote! { impl IntoIterator<Item=#iter_type> }).unwrap())
Some(parse2(quote! { impl IntoIterator<Item=#iter_type> }).expect("Could not parse `IntoIterator` type"))
}
FieldConfigProperty::Expression { input_type, .. }
if input_type.is_some() =>
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub(crate) enum CtorAttribute {
Const,
DefaultAll,
Default,
IntoAll,
}

impl Default for CtorDefinition {
Expand Down
17 changes: 15 additions & 2 deletions src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use syn::parse::{Parse, ParseStream};
use syn::token::{Comma, Const};

use crate::{consume_delimited, CtorAttribute, CtorDefinition, try_parse_attributes_with_default};
use crate::constants::{DEFAULT_CTOR_ERR_MSG, ENUM_VARIATION_PROP_NONE as NONE, NESTED_PROP_ALL as ALL, STRUCT_PROP_DEFAULT as DEFAULT};
use crate::constants::{DEFAULT_CTOR_ERR_MSG, ENUM_VARIATION_PROP_NONE as NONE, NESTED_PROP_ALL as ALL, STRUCT_PROP_DEFAULT as DEFAULT, STRUCT_PROP_INTO as INTO};
use crate::fields::generate_ctor_meta;

pub(crate) struct CtorStructConfiguration {
Expand Down Expand Up @@ -69,7 +69,7 @@ impl Parse for CtorStructConfiguration {
DEFAULT => {
if let Ok(true) =
consume_delimited(input, Delimiter::Parenthesis, |buffer| {
Ok(buffer.parse::<Ident>()?.to_string() == ALL)
Ok(buffer.parse::<Ident>()? == ALL)
})
{
attributes.insert(CtorAttribute::DefaultAll);
Expand All @@ -78,6 +78,19 @@ impl Parse for CtorStructConfiguration {
}
_ => {}
}

if let Ok(Some(attribute)) = consume_delimited(input, Delimiter::Parenthesis, |buffer| {
let ident = buffer.parse::<Ident>()?;
if ident == DEFAULT {
return Ok(Some(CtorAttribute::DefaultAll))
}
if ident == INTO {
return Ok(Some(CtorAttribute::IntoAll))
}
Ok(None)
}) {
attributes.insert(attribute);
}

CtorDefinition {
visibility: Visibility::Inherited,
Expand Down
17 changes: 17 additions & 0 deletions tests/struct_ctor_config_default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,20 @@ fn test_struct_implement_default_all_members() {
);
}

#[derive(ctor, Debug, PartialEq)]
#[ctor(with_defaults(default), new(into))]
struct CtorNestedProperties {
name: String,
#[ctor(expr!(value + 1))]
value: u32
}

#[test]
fn test_struct_with_nested_properties() {
let test1 = CtorNestedProperties::with_defaults();
assert_eq!(CtorNestedProperties { name: String::default(), value: 0 }, test1);

let test2 = CtorNestedProperties::new("FooBar", 30);
assert_eq!(CtorNestedProperties { name: String::from("FooBar"), value: 31 }, test2)

}

0 comments on commit 43b0b7e

Please sign in to comment.