Skip to content

Commit 06c1a86

Browse files
authored
Merge pull request #9 from ImaMapleTree/feature/release-1.0.0-stabilize-and-support-union
Implement Union Constructors
2 parents 743255c + 5d178b7 commit 06c1a86

File tree

9 files changed

+349
-73
lines changed

9 files changed

+349
-73
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "derive-ctor"
3-
version = "0.2.3"
4-
description = "Adds `#[derive(ctor)]` which allows for the auto-generation of struct and enum constructors."
3+
version = "1.0.0"
4+
description = "Adds `#[derive(ctor)]` which allows for the auto-generation of struct, enum, and union constructors."
55
keywords = ["derive", "macro", "trait", "procedural", "no_std"]
66
authors = ["Evan Cowin"]
77
license = "MIT"
@@ -11,9 +11,10 @@ exclude = [".github/*", ".gitignore"]
1111
categories = ["no-std", "rust-patterns"]
1212

1313
[features]
14-
default = ["structs", "enums"]
14+
default = ["structs", "enums", "unions"]
1515
enums = ["dep:heck"]
1616
structs = []
17+
unions = []
1718

1819
[lib]
1920
proc-macro = true

README.md

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# derive-ctor
22

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

77
## Features
8-
- Automatically generate a constructor method for structs and enums with `#[derive(ctor)]`.
8+
- Automatically generate a constructor method for structs, enums, and unions with `#[derive(ctor)]`.
99
- Customize the name and visibility of the auto-generated constructor using `#[ctor(visibility method_name)]`.
1010
- Supports const constructors by adding the "const" keyword.
1111
- Provide a list of names to generate multiple constructors.
@@ -25,7 +25,7 @@ Add `derive-ctor` to your `Cargo.toml`:
2525

2626
```toml
2727
[dependencies]
28-
derive-ctor = "0.2.3"
28+
derive-ctor = "1.0.0"
2929
```
3030

3131
Annotate your struct with `#[derive(ctor)]` to automatically generate a `new` constructor:
@@ -97,7 +97,7 @@ struct OtherStruct {
9797
let default2: OtherStruct = Default::default();
9898
```
9999

100-
## Enum Configurations
100+
## Enum and Union Configurations
101101

102102
By default, a constructor will be generated for each variant. This constructor by default will match the name of its
103103
respective variant and will be public. This default behaviour can be changed by annotating the enum with
@@ -146,6 +146,27 @@ let v3 = MyEnum::new_variant3();
146146

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

149+
Unions express the same behaviours as enums except applicable to the fields of the union rather than the variants of
150+
an enum.
151+
152+
```rust
153+
use derive_ctor::ctor;
154+
155+
#[derive(ctor)]
156+
#[ctor(prefix = new, vis = pub(crate))]
157+
union MyUnion {
158+
#[ctor(const new)]
159+
v1: i32,
160+
v2: f32,
161+
v3: u32
162+
}
163+
164+
const VAL: MyUnion = MyUnion::new(100);
165+
let v2 = MyUnion::new_v2(123.231);
166+
let v3 = MyUnion::new_v3(414224);
167+
```
168+
169+
149170
## Field Configurations
150171

151172
Fields can also be annotated with `#[ctor(PROPERTY)]` to change their behaviour in the generated methods.

src/enums.rs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ use alloc::string::ToString;
44
use alloc::vec;
55
use alloc::vec::Vec;
66

7-
#[cfg(feature = "enums")]
87
use heck::ToSnakeCase;
98

109
use crate::constants::{CONFIG_PROP_ERR_MSG, ENUM_PROP_VIS as VIS, ENUM_PROP_VISIBILITY as VISIBILITY, ENUM_PROP_PREFIX as PREFIX};
11-
use crate::structs::{CtorAttribute, CtorDefinition, CtorStructConfiguration, };
12-
use crate::try_parse_attributes_with_default;
10+
use crate::structs::CtorStructConfiguration;
11+
use crate::{CtorAttribute, CtorDefinition, try_parse_attributes_with_default};
12+
use crate::fields::generate_ctor_meta;
13+
1314
use proc_macro2::Span;
1415
use quote::quote;
1516
use syn::parse::{Parse, ParseStream};
1617
use syn::punctuated::Punctuated;
1718
use syn::token::{Comma, Pub};
1819
use syn::{token, Data, DeriveInput, Error, Fields, Generics, Ident, Variant, Visibility};
19-
use crate::fields::generate_ctor_meta;
2020

21-
static ENUM_CTOR_PROPS: &str = "\"prefix\", \"visibility\", \"vis\"";
21+
const ENUM_CTOR_PROPS: &str = "\"prefix\", \"visibility\", \"vis\"";
2222

2323
enum EnumConfigItem {
2424
Visibility { visibility: Visibility },
@@ -98,14 +98,6 @@ impl Parse for EnumConfigItem {
9898
}
9999
}
100100

101-
#[cfg(not(feature = "enums"))]
102-
pub(crate) fn create_enum_token_stream(derive_input: DeriveInput) -> TokenStream {
103-
use syn::spanned::Spanned;
104-
TokenStream::from(Error::new(Span::call_site(),
105-
"\"enums\" feature must be enabled to use #[derive(ctor)] on enums.").to_compile_error())
106-
}
107-
108-
#[cfg(feature = "enums")]
109101
pub(crate) fn create_enum_token_stream(derive_input: DeriveInput) -> TokenStream {
110102
if let Data::Enum(data) = derive_input.data {
111103
let configuration = match try_parse_attributes_with_default(&derive_input.attrs, || {
@@ -125,7 +117,6 @@ pub(crate) fn create_enum_token_stream(derive_input: DeriveInput) -> TokenStream
125117
panic!("Expected Enum data")
126118
}
127119

128-
#[cfg(feature = "enums")]
129120
fn create_ctor_enum_impl(
130121
ident: Ident,
131122
generics: Generics,
@@ -202,7 +193,6 @@ fn create_ctor_enum_impl(
202193
})
203194
}
204195

205-
#[cfg(feature = "enums")]
206196
fn convert_to_snakecase(method_ident: Ident) -> Result<Ident, Error> {
207197
let ident_string = method_ident.to_string();
208198
let trimmed_start_str = ident_string.trim_start_matches('_');
@@ -218,7 +208,7 @@ fn convert_to_snakecase(method_ident: Ident) -> Result<Ident, Error> {
218208
syn::parse_str(&snake_case)
219209
}
220210

221-
#[test] #[cfg(feature = "enums")]
211+
#[test]
222212
fn test_convert_to_snakecase() {
223213
assert_eq!(
224214
convert_to_snakecase(Ident::new("A", Span::mixed_site())).unwrap(),

src/fields.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ use crate::constants::{
1616
CONFIG_PROP_ERR_MSG, FIELD_PROP_CLONED as CLONED, FIELD_PROP_DEFAULT as DEFAULT,
1717
FIELD_PROP_EXPR as EXPR, FIELD_PROP_INTO as INTO, FIELD_PROP_ITER as ITER,
1818
};
19-
use crate::structs::CtorAttribute;
20-
use crate::structs::CtorAttribute::DefaultAll;
21-
use crate::{consume_delimited, is_phantom_data, try_parse_attributes};
19+
use crate::{consume_delimited, CtorAttribute, is_phantom_data, try_parse_attributes};
2220

2321
const FIELD_PROPS: &str = "\"cloned\", \"default\", \"expr\", \"into\", \"iter\"";
2422

@@ -219,7 +217,7 @@ pub(crate) fn generate_ctor_meta(
219217
let mut gen_configuration = None;
220218

221219
match &configuration {
222-
None if ctor_attributes.contains(&DefaultAll) => {
220+
None if ctor_attributes.contains(&CtorAttribute::DefaultAll) => {
223221
gen_configuration = Some(FieldConfigProperty::Default)
224222
}
225223
None if is_phantom_data(&field.ty) => {

src/lib.rs

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,89 @@
11
#![no_std]
22
#![doc = include_str!("../README.md")]
3+
#![allow(dead_code)]
34

45
extern crate alloc;
56
use alloc::format;
67
use alloc::string::ToString;
8+
use alloc::collections::BTreeSet as HashSet;
79

810
use crate::constants::CTOR_WORD;
11+
#[cfg(feature = "enums")]
912
use crate::enums::create_enum_token_stream;
13+
#[cfg(feature = "structs")]
14+
use crate::structs::create_struct_token_stream;
15+
#[cfg(feature = "unions")]
16+
use crate::unions::create_union_token_stream;
17+
1018
use proc_macro::TokenStream;
11-
use proc_macro2::Delimiter;
19+
use proc_macro2::{Delimiter, Ident, Span};
1220
use quote::ToTokens;
13-
use structs::create_struct_token_stream;
1421
use syn::parse::discouraged::AnyDelimiter;
1522
use syn::parse::Parse;
1623
use syn::parse::ParseStream;
17-
use syn::parse_macro_input;
24+
use syn::{parse_macro_input, Visibility};
1825
use syn::spanned::Spanned;
1926
use syn::Attribute;
2027
use syn::Data;
2128
use syn::DeriveInput;
2229
use syn::Error;
30+
use syn::token::Pub;
2331
use syn::Type;
2432

33+
2534
pub(crate) mod constants;
35+
#[cfg(feature = "enums")]
2636
pub(crate) mod enums;
37+
#[cfg(any(feature = "enums", feature = "structs", feature = "unions"))]
2738
pub(crate) mod fields;
39+
#[cfg(feature = "structs")]
2840
pub(crate) mod structs;
41+
#[cfg(feature = "unions")]
42+
pub(crate) mod unions;
43+
44+
pub(crate) struct CtorDefinition {
45+
pub(crate) visibility: Visibility,
46+
pub(crate) ident: Ident,
47+
pub(crate) attrs: HashSet<CtorAttribute>,
48+
}
49+
50+
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord)]
51+
pub(crate) enum CtorAttribute {
52+
Const,
53+
DefaultAll,
54+
Default,
55+
}
56+
57+
impl Default for CtorDefinition {
58+
fn default() -> Self {
59+
Self {
60+
visibility: Visibility::Public(Pub {
61+
span: Span::call_site(),
62+
}),
63+
ident: Ident::new("new", Span::mixed_site()),
64+
attrs: Default::default(),
65+
}
66+
}
67+
}
68+
69+
#[cfg(not(feature = "enums"))]
70+
pub(crate) fn create_enum_token_stream(_derive_input: DeriveInput) -> TokenStream {
71+
use proc_macro2::Span;
72+
TokenStream::from(Error::new(Span::call_site(),
73+
"\"enums\" feature must be enabled to use #[derive(ctor)] on enums.").to_compile_error())
74+
}
75+
76+
#[cfg(not(feature = "structs"))]
77+
pub(crate) fn create_struct_token_stream(_derive_input: DeriveInput) -> TokenStream {
78+
TokenStream::from(Error::new(Span::call_site(),
79+
"\"structs\" feature must be enabled to use #[derive(ctor)] on structs.").to_compile_error())
80+
}
81+
82+
#[cfg(not(feature = "unions"))]
83+
pub(crate) fn create_union_token_stream(_derive_input: DeriveInput) -> TokenStream {
84+
TokenStream::from(Error::new(Span::call_site(),
85+
"\"unions\" feature must be enabled to use #[derive(ctor)] on unions.").to_compile_error())
86+
}
2987

3088
#[proc_macro_derive(ctor, attributes(ctor))]
3189
pub fn derive_ctor(input: TokenStream) -> TokenStream {
@@ -34,9 +92,7 @@ pub fn derive_ctor(input: TokenStream) -> TokenStream {
3492
match &derive_input.data {
3593
Data::Struct(_) => create_struct_token_stream(derive_input),
3694
Data::Enum(_) => create_enum_token_stream(derive_input),
37-
Data::Union(_) => TokenStream::from(
38-
Error::new(derive_input.span(), "Unions are not yet supported by ctor").to_compile_error(),
39-
),
95+
Data::Union(_) => create_union_token_stream(derive_input)
4096
}
4197
}
4298

src/structs.rs

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,43 +6,16 @@ use alloc::vec;
66
use alloc::vec::Vec;
77
use proc_macro::TokenStream;
88

9-
use proc_macro2::{Delimiter, Span};
9+
use proc_macro2::Delimiter;
1010
use quote::quote;
1111
use syn::{Data, DeriveInput, Error, Fields, Generics, Ident, Visibility};
1212
use syn::parse::{Parse, ParseStream};
13-
use syn::token::{Comma, Const, Pub};
13+
use syn::token::{Comma, Const};
1414

15-
use CtorAttribute::DefaultAll;
16-
17-
use crate::{consume_delimited, try_parse_attributes_with_default};
15+
use crate::{consume_delimited, CtorAttribute, CtorDefinition, try_parse_attributes_with_default};
1816
use crate::constants::{DEFAULT_CTOR_ERR_MSG, ENUM_VARIATION_PROP_NONE as NONE, NESTED_PROP_ALL as ALL, STRUCT_PROP_DEFAULT as DEFAULT};
1917
use crate::fields::generate_ctor_meta;
2018

21-
pub(crate) struct CtorDefinition {
22-
pub(crate) visibility: Visibility,
23-
pub(crate) ident: Ident,
24-
pub(crate) attrs: HashSet<CtorAttribute>,
25-
}
26-
27-
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord)]
28-
pub(crate) enum CtorAttribute {
29-
Const,
30-
DefaultAll,
31-
Default,
32-
}
33-
34-
impl Default for CtorDefinition {
35-
fn default() -> Self {
36-
Self {
37-
visibility: Visibility::Public(Pub {
38-
span: Span::call_site(),
39-
}),
40-
ident: Ident::new("new", Span::mixed_site()),
41-
attrs: Default::default(),
42-
}
43-
}
44-
}
45-
4619
pub(crate) struct CtorStructConfiguration {
4720
pub(crate) definitions: Vec<CtorDefinition>,
4821
pub(crate) is_none: bool,
@@ -99,7 +72,7 @@ impl Parse for CtorStructConfiguration {
9972
Ok(buffer.parse::<Ident>()?.to_string() == ALL)
10073
})
10174
{
102-
attributes.insert(DefaultAll);
75+
attributes.insert(CtorAttribute::DefaultAll);
10376
}
10477
attributes.insert(CtorAttribute::Default);
10578
}
@@ -128,13 +101,6 @@ impl Parse for CtorStructConfiguration {
128101
}
129102
}
130103

131-
#[cfg(not(feature = "structs"))]
132-
pub(crate) fn create_struct_token_stream(derive_input: DeriveInput) -> TokenStream {
133-
TokenStream::from(Error::new(Span::call_site(),
134-
"\"structs\" feature must be enabled to use #[derive(ctor)] on structs.").to_compile_error())
135-
}
136-
137-
#[cfg(feature = "structs")]
138104
pub(crate) fn create_struct_token_stream(derive_input: DeriveInput) -> TokenStream {
139105
if let Data::Struct(data) = derive_input.data {
140106
let configuration = match try_parse_attributes_with_default(&derive_input.attrs, || {

0 commit comments

Comments
 (0)