Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/release 0.2.0 add enum support #5

Merged
merged 4 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 = "0.1.1"
version = "0.2.0"
description = "Adds `#[derive(ctor)]` which allows for the auto-generation of a constructor."
keywords = ["derive", "macro", "trait", "procedural", "no_std"]
authors = ["Evan Cowin"]
Expand Down
125 changes: 113 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
# derive-ctor

`derive-ctor` is a Rust procedural macro crate that allows you to easily generate constructor methods for your structs. With the `#[derive(ctor)]` attribute, you can automatically create a constructor for all fields in the struct. The crate also provides various options to customize the generated constructor methods.
`derive-ctor` is a Rust procedural macro crate that allows you to easily generate constructor methods for your structs. With the `#[derive(ctor)]` attribute, you can automatically create a constructor(s) for structs and enums. The crate also provides various options to customize the generated constructor methods.

## Features

- Automatically generate a constructor method for all fields in a struct with `#[derive(ctor)]`.
- Automatically generate a constructor method for structs and enums with `#[derive(ctor)]`.
- Customize the name and visibility of the auto-generated constructor using `#[ctor(visibility method_name)]`.
- Provide a list of names to generate multiple constructors.
- Supports const constructors by adding the "const" keyword.
- Provide a list of names to generate multiple constructors.
- Customize field behavior in the constructor with the following attributes:
- `#[ctor(cloned)]` - Changes the parameter type to accept a reference type which is then cloned into the created struct.
- `#[ctor(default)]` - Exclude the field from the generated method and use its default value.
- `#[ctor(expr(EXPRESSION))]` - Exclude the field from the generated method and use the defined expression as its default value.
- `#[ctor(impl)]` - Change the parameter type for the generated method to `impl Into<Type>`.
- Use `#[ctor(expr!(EXPRESSION))]` to add the annotated field as a required parameter, allowing the expression to reference itself.
- Use `#[ctor(expr(TYPE -> EXPRESSION))]` to add a parameter with the specified type, which will be used to generate the final field value.
- `#[ctor(into)]` - Change the parameter type for the generated method to `impl Into<Type>`.
- `#[ctor(iter(FROM_TYPE))]` - Change the parameter type for the generated method to `impl IntoIterator<Item=FROM_TYPE>`.
- Support no-std via `features = ["no-std"]`

## Basic Usage
Expand Down Expand Up @@ -41,7 +46,7 @@ struct MyStruct {
let my_struct = MyStruct::new(1, String::from("Foo"));
```

## Configurations
## Struct Configurations

You can modify the name and visibility of the generated method, and define additional
constructors by using the `#[ctor]` attribute on the target struct after `ctor` is derived.
Expand All @@ -53,18 +58,84 @@ These methods all inherit their respective visibilities defined within the `#[ct
use derive_ctor::ctor;

#[derive(ctor)]
#[ctor(pub new, pub(crate) with_defaults, internal)]
#[ctor(pub new, pub(crate) with_defaults, const internal)]
struct MyStruct {
field1: i32,
field2: String
}
```

### Field Configurations
## Enum Configurations

By default a constructor will be generated for each variant. This constructor by default will match the name of its
respective variant and will be public. This default behaviour can be changed by annotating the enum with
`#[ctor(prefix = PREFIX, visibility = VISIBILITY)]`. Note that both parameters are optional within the attribute.
Specifying this attribute will change the **default** generated method for each variant, however, each variant
can additionally define its own configuration which overrides the one defined by the enum.

### Default variant constructor example

```rust
use derive_ctor::ctor;

#[derive(ctor)]
enum MyEnum {
Variant1,
Variant2(i32),
Variant3 { value: bool }
}

let v1 = MyEnum::variant1();
let v2 = MyEnum::variant2(100);
let v3 = MyEnum::variant3(true);
```

### Configured variant constructor example
Variant constructor configuration is identical to struct constructor configuration. Refer to the below for
sample syntax or go-back to the struct constructor configuration for more information.

```rust
use derive_ctor::ctor;

#[derive(ctor)]
#[ctor(prefix = new, vis = pub(crate))]
enum MyEnum {
#[ctor(const pub v1, other)]
Variant1,
Variant2,
Variant3
}

const v1_1: MyEnum = MyEnum::v1();
let v1_2 = MyEnum::other();
let v2 = MyEnum::new_variant2();
let v3 = MyEnum::new_variant3();
```

If a variant is derived with `#[ctor(none)]` it will **not** have a constructor generated for it.

## Field Configurations

Fields can also be annotated with `#[ctor(PROPERTY)]` to change their behaviour in the generated methods.
**These configurations work for ALL enum-types and structs!**
The following are the available properties that can be used with the field-attributes

`#[ctor(cloned)]` - This property creates a parameter that accepts a type reference of the annotated field and
then clones it to generate the final value.
```rust
use derive_ctor::ctor;

#[derive(ctor)]
struct MyStruct {
field1: i32,
#[ctor(cloned)]
field2: String
}

let string = String::from("Foo");
let my_struct = MyStruct::new(100, &string);
```

`#[ctor(default)]` - This property excludes the annotated field from the constructor and uses its default value.
```rust
use derive_ctor::ctor;
Expand All @@ -79,34 +150,64 @@ struct MyStruct {
let my_struct = MyStruct::new(100);
```

`#[ctor(impl)]` - This property modifies the parameter type of the annotated field for the generated method
`#[ctor(into)]` - This property modifies the parameter type of the annotated field for the generated method
converting it from `Type` -> `impl Into<Type>`.
```rust
use derive_ctor::ctor;

#[derive(ctor)]
struct MyStruct {
field1: i32,
#[ctor(impl)] // the parameter type will now be impl Into<String> instead of String
#[ctor(into)] // the parameter type will now be impl Into<String> instead of String
field2: String
}

let my_struct = MyStruct::new(100, "Foo");
```

`#[ctor(expr(VALUE))]` - This property excludes the annotated field from the constructor and utilizes the defined expression
`#[ctor(expr(EXPRESSION))]` - This property excludes the annotated field from the constructor and utilizes the defined expression
to generate its value.

**Alternatives:**

- `#[ctor(expr!(EXPRESSION))]` - Unlike the above attribute, this attribute will add the annotated field as a required parameter
for the given constructor, this allows for the provided EXPRESSION to reference the parameter and modify the passed value.
- `#[ctor(expr(TYPE -> EXPRESSION))]` - This attribute behaves similar to the variation above, however, the required parameter
type will be of the type provided in the attribute, thus allowing for a constructor to accept and map a parameter from one type
to the type used by the struct field.

```rust
use derive_ctor::ctor;

#[derive(ctor)]
struct MyStruct {
field1: i32,
#[ctor(expr(String::from("Foo")))]
field2: String
field2: String,
#[ctor(expr!(field3 + 100))]
field3: u32,
#[ctor(expr(i32 -> field4 < 0))]
field4: bool
}

let my_struct = MyStruct::new(100, 5, -20); // generates MyStruct { field1: 100, field2: "foo", field3: 105, field4: true }
```

`#[ctor(iter(TYPE))]` - This property adds a parameter with the type: `impl IntoIterator<Item=TYPE>` and then generates
the annotated struct value by calling `.into_iter().collect()` on the parameter value.

```rust
use std::collections::HashSet;
use derive_ctor::ctor;

#[derive(ctor)]
struct MyStruct {
field1: i32,
#[ctor(iter(usize))]
field2: HashSet<usize>
}

let my_struct = MyStruct::new(100); // generates MyStruct { field1: 100, field2: "foo" }
let my_struct = MyStruct::new(0, vec![1, 1, 2, 3, 4]);
```

### Advanced Configuration
Expand Down
Loading