Skip to content
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
27 changes: 20 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,46 @@
# Changelog

## unreleased

* Propagate `#[repr(C)]` onto generated structs. ([#11][#11], [#12][#12])
* Update to `syn` `2`. ([#12][#12])

[#11]: https://github.com/azriel91/enum_variant_type/issues/11
[#12]: https://github.com/azriel91/enum_variant_type/pull/12


## 0.3.1 (2021-12-22)

* Import all items from parent scope when generating structs in submodule. ([#9])
* Import all items from parent scope when generating structs in submodule. ([#9][#9])

[#9]: https://github.com/azriel91/enum_variant_type/pull/9

[#9]: https://github.com/azriel91/enum_variant_type/pulls/9

## 0.3.0 (2021-12-18)

* `#[evt(derive(..))]` on enum adds derives on every variant. ([#6], [#7])
* `#[evt(module = "module1")]` generates structs inside `mod module1`. ([#5], [#7])
* `#[evt(implement_marker_traits(MarkerTrait1))]` on enum generates `impl MarkerTrait1` for all generated structs. ([#7])
* `#[evt(derive(..))]` on enum adds derives on every variant. ([#6][#6], [#7][#7])
* `#[evt(module = "module1")]` generates structs inside `mod module1`. ([#5][#5], [#7][#7])
* `#[evt(implement_marker_traits(MarkerTrait1))]` on enum generates `impl MarkerTrait1` for all generated structs. ([#7][#7])

[#5]: https://github.com/azriel91/enum_variant_type/issues/5
[#6]: https://github.com/azriel91/enum_variant_type/issues/6
[#7]: https://github.com/azriel91/enum_variant_type/pulls/7
[#7]: https://github.com/azriel91/enum_variant_type/pull/7


## 0.2.1 (2021-04-24)

* `no-std` support by default. ([#2], [#3])
* `no-std` support by default. ([#2][#2], [#3][#3])

[#2]: https://github.com/azriel91/enum_variant_type/issues/2
[#3]: https://github.com/azriel91/enum_variant_type/pull/3


## 0.2.0 (2020-01-13)

* Allow variants to be skipped using `#[evt(skip)]`.
* ***Breaking:*** `#[evt(..)]` specifies the attributes to attach to the generated type (previously `#[evt_attr(..)]`).


## 0.1.0 (2020-01-10)

* Generates unit, tuple, named struct for each enum variant.
Expand Down
13 changes: 8 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ path = "src/lib.rs"
proc-macro = true

[dependencies]
proc-macro2 = "1.0.34"
proc_macro_roids = "0.7.0"
quote = "1.0.10"
syn = { version = "1.0.82", features = ["extra-traits", "visit"] }
proc-macro2 = "1.0.106"
proc_macro_roids = "0.8.0"
quote = "1.0.44"
syn = { version = "2.0.117", features = ["extra-traits", "visit"] }

[dev-dependencies]
pretty_assertions = "1.0.0"
pretty_assertions = "1.4.1"

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(tarpaulin_include)"] }
2 changes: 1 addition & 1 deletion rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
format_code_in_doc_comments = true
merge_imports = true
imports_granularity = "crate"
177 changes: 131 additions & 46 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ use proc_macro2::{Ident, Span};
use proc_macro_roids::{namespace_parameters, FieldsExt};
use quote::quote;
use syn::{
parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Field, Fields, Lit,
Meta, NestedMeta, Path,
parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Field, Fields, LitStr,
Meta, Path,
};

/// Attributes that should be copied across.
Expand Down Expand Up @@ -187,52 +187,70 @@ fn enum_variant_type_impl(ast: DeriveInput) -> proc_macro2::TokenStream {
let mut wrap_in_module = None::<Ident>;
let mut derive_for_all_variants = None::<Attribute>;
let mut marker_trait_paths = Vec::<Path>::new();
let mut repr_c = false;

for attr in ast.attrs.iter() {
if attr.path.is_ident("evt") {
if let Ok(Meta::List(list)) = attr.parse_meta() {
for item in list.nested.iter() {
match item {
NestedMeta::Meta(Meta::NameValue(name_value)) => {
if let (true, Lit::Str(lit_str)) =
(name_value.path.is_ident("module"), &name_value.lit)
{
wrap_in_module =
Some(Ident::new(&lit_str.value(), Span::call_site()));
} else {
panic!("Expected `evt` attribute argument in the form: `#[evt(module = \"some_module_name\")]`");
}
}
NestedMeta::Meta(Meta::List(list)) => {
if list.path.is_ident("derive") {
let items = list.nested.iter().map(|nested_meta| {
if let NestedMeta::Meta(Meta::Path(path)) = nested_meta {
path.clone()
} else {
panic!("Expected `evt` attribute argument in the form: `#[evt(derive(Clone, Debug))]`");
}
});
derive_for_all_variants = Some(parse_quote! {
#[derive( #(#items),* )]
});
} else if list.path.is_ident("implement_marker_traits") {
marker_trait_paths = list.nested
.iter()
.map(|nested| if let NestedMeta::Meta(Meta::Path(path)) = nested {
path.clone()
} else {
panic!("Expected `evt` attribute argument in the form #[evt(implement_marker_traits(MarkerTrait1, MarkerTrait2))]");
}).collect();
}
}
_ => {
panic!("Unexpected usage of `evt` attribute, please see examples at:\n<https://docs.rs/enum_variant_type/>")
}
if attr.path().is_ident("repr") {
// wrap each enum struct in "repr(C)" ?
if let Meta::List(list) = &attr.meta {
list.parse_nested_meta(|parse_nested_meta| {
if parse_nested_meta.path.is_ident("C") {
repr_c = true;
}
}
} else {
panic!("Unexpected usage of `evt` attribute, please see examples at:\n<https://docs.rs/enum_variant_type/>")
Ok(())
})
.unwrap_or_else(|e| panic!("Failed to parse repr attribute. Error: {}", e));
}
} else if attr.path().is_ident("evt") {
attr.parse_nested_meta(|nested_meta| {
if nested_meta.path.is_ident("module") {
// `#[evt(module = \"some_module_name\")]`
let module_name: LitStr = nested_meta
.value()
.and_then(|value| value.parse())
.unwrap_or_else(|e| {
panic!(
"Expected `evt` attribute argument in the form: \
`#[evt(module = \"some_module_name\")]`. Error: {}",
e
)
});

wrap_in_module = Some(Ident::new(&module_name.value(), Span::call_site()));
return Ok(());
}
// `#[evt(derive(Clone, Debug))]`
if nested_meta.path.is_ident("derive") {
let mut items = Vec::new();
nested_meta.parse_nested_meta(|parse_nested_meta| {
items.push(parse_nested_meta.path);
Ok(())
})?;

derive_for_all_variants = Some(parse_quote! {
#[derive( #(#items),* )]
});
return Ok(());
}

// `#[evt(implement_marker_traits(MarkerTrait1, MarkerTrait2))]`
if nested_meta.path.is_ident("implement_marker_traits") {
nested_meta.parse_nested_meta(|parse_nested_meta| {
marker_trait_paths.push(parse_nested_meta.path);
Ok(())
})?;

return Ok(());
}

panic!(
"Unexpected usage of `evt` attribute, please see examples at:\n\
<https://docs.rs/enum_variant_type/>"
)
})
.unwrap_or_else(|e| {
panic!("Failed to process evt attribute. Error: {}", e);
});
}
}

Expand All @@ -251,12 +269,12 @@ fn enum_variant_type_impl(ast: DeriveInput) -> proc_macro2::TokenStream {
.filter(|attribute| {
ATTRIBUTES_TO_COPY
.iter()
.any(|attr_to_copy| attribute.path.is_ident(attr_to_copy))
.any(|attr_to_copy| attribute.path().is_ident(attr_to_copy))
})
.collect::<Vec<&Attribute>>();

let evt_meta_lists = namespace_parameters(&variant.attrs, &ns);
let variant_struct_attrs = evt_meta_lists
let mut variant_struct_attrs = evt_meta_lists
.into_iter()
.fold(
proc_macro2::TokenStream::new(),
Expand All @@ -265,6 +283,13 @@ fn enum_variant_type_impl(ast: DeriveInput) -> proc_macro2::TokenStream {
attrs_tokens
},
);

if repr_c {
variant_struct_attrs.extend(quote! {
#[repr(C)]
})
}

let variant_fields = &variant.fields;

// Need to attach visibility modifier to fields.
Expand Down Expand Up @@ -683,4 +708,64 @@ mod tests {

assert_eq!(expected_tokens.to_string(), actual_tokens.to_string());
}

#[test]
fn derive_marker_repr() {
let ast: DeriveInput = parse_quote! {
#[derive(Debug)]
#[repr(C)]
pub enum MyEnum {
A { i: i64 },
B { i: i64 },
}
};

let actual_tokens = enum_variant_type_impl(ast);
let expected_tokens = quote! {

#[repr(C)]
pub struct A { pub i: i64, }

impl core::convert::From<A> for MyEnum {
fn from(variant_struct: A) -> Self {
let A { i, } = variant_struct;
MyEnum::A { i, }
}
}

impl core::convert::TryFrom<MyEnum> for A {
type Error = MyEnum;
fn try_from(enum_variant: MyEnum) -> Result<Self, Self::Error> {
if let MyEnum::A { i, } = enum_variant {
core::result::Result::Ok(A { i, })
} else {
core::result::Result::Err(enum_variant)
}
}
}

#[repr(C)]
pub struct B { pub i: i64, }

impl core::convert::From<B> for MyEnum {
fn from(variant_struct: B) -> Self {
let B { i, } = variant_struct;
MyEnum::B { i, }
}
}

impl core::convert::TryFrom<MyEnum> for B {
type Error = MyEnum;
fn try_from(enum_variant: MyEnum) -> Result<Self, Self::Error> {
if let MyEnum::B { i, } = enum_variant {
core::result::Result::Ok(B { i, })
} else {
core::result::Result::Err(enum_variant)
}
}
}
};

assert_eq!(expected_tokens.to_string(), actual_tokens.to_string());
}
}