Skip to content

Commit 082405b

Browse files
committed
Make only the Encapsulate derive macros recognize the enum-level #[enumcapsulate(exclude(…))] helper macro
1 parent a69a28b commit 082405b

File tree

6 files changed

+127
-168
lines changed

6 files changed

+127
-168
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Please make sure to add your changes to the appropriate categories:
2525
### Changed
2626

2727
- Bumped MSRV from "1.74.0" to "1.78.0".
28+
- Made the enum-level `#[enumcapsulate(exclude(…))]` helper macros only have an effect on derives, when orchestrated through the `Encapsulate` derive macro.
2829

2930
### Deprecated
3031

macros/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ Exclude this variant from trait derivation.
9898
9999
Exclude variant from specific `enumcapsulate` derive macros.
100100
101+
> [!IMPORTANT]
102+
> This attribute is only recognized by the `Encapsulate` umbrella derive macro.
103+
101104
If you wish to opt out of a select few of `Encapsulate`'s trait derives,
102105
then you can do so by use of an `#[enumcapsulate(exclude(…))]` attribute:
103106

macros/src/config.rs

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ pub(crate) type VariantExcludeConfig = MacroSelectionConfig;
5858
pub(crate) type VariantIncludeConfig = MacroSelectionConfig;
5959

6060
#[derive(Clone, Default)]
61-
pub(crate) struct EnumConfig {
61+
pub(crate) struct EncapsulateEnumConfig {
6262
// #[enumcapsulate(exclude(…))]
6363
pub exclude: Option<EnumExcludeConfig>,
6464
}
6565

66-
impl EnumConfig {
66+
impl EncapsulateEnumConfig {
6767
pub fn is_included(&self, name: &str) -> bool {
6868
!self.is_excluded(name)
6969
}
@@ -76,6 +76,9 @@ impl EnumConfig {
7676
}
7777
}
7878

79+
#[derive(Clone, Default)]
80+
pub(crate) struct EnumConfig {}
81+
7982
#[derive(Clone, Default)]
8083
pub(crate) struct VariantConfig {
8184
// #[enumcapsulate(exclude(…))]
@@ -89,17 +92,16 @@ pub(crate) struct VariantConfig {
8992
}
9093

9194
impl VariantConfig {
92-
pub fn is_excluded(&self, name: &str, config: &EnumConfig) -> bool {
93-
if self.is_excluded_explicitly(name) {
94-
assert!(!self.is_included_explicitly(name));
95-
return true;
96-
}
97-
95+
pub fn is_excluded(&self, name: &str) -> bool {
9896
if self.is_included_explicitly(name) {
9997
return false;
10098
}
10199

102-
config.is_excluded(name)
100+
if self.is_excluded_explicitly(name) {
101+
return true;
102+
}
103+
104+
false
103105
}
104106

105107
pub fn is_excluded_explicitly(&self, name: &str) -> bool {
@@ -135,14 +137,21 @@ impl VariantConfig {
135137
}
136138
}
137139

138-
pub(crate) fn config_for_enum(enum_item: &syn::ItemEnum) -> Result<EnumConfig, syn::Error> {
139-
let mut config = EnumConfig::default();
140+
pub(crate) fn encapsulate_config_for_enum(
141+
enum_item: &syn::ItemEnum,
142+
) -> Result<EncapsulateEnumConfig, syn::Error> {
143+
let mut config = EncapsulateEnumConfig::default();
140144

141145
parse_enumcapsulate_attrs(&enum_item.attrs, |meta| {
142146
if meta.path.is_ident(attr::EXCLUDE) {
143147
// #[enumcapsulate(exclude(…))]
144148

145149
let mut exclude = config.exclude.take().unwrap_or_default();
150+
151+
if exclude.is_empty() {
152+
return Err(meta.error("expected list"));
153+
}
154+
146155
exclude.extend_idents(macro_selection_config_for_enum(&meta)?.idents);
147156

148157
config.exclude = Some(exclude);
@@ -156,6 +165,24 @@ pub(crate) fn config_for_enum(enum_item: &syn::ItemEnum) -> Result<EnumConfig, s
156165
Ok(config)
157166
}
158167

168+
pub(crate) fn config_for_enum(enum_item: &syn::ItemEnum) -> Result<EnumConfig, syn::Error> {
169+
let config = EnumConfig::default();
170+
171+
parse_enumcapsulate_attrs(&enum_item.attrs, |meta| {
172+
if meta.path.is_ident(attr::EXCLUDE) {
173+
// #[enumcapsulate(exclude(…))]
174+
175+
// Ignored.
176+
} else {
177+
return Err(meta.error("unrecognized attribute"));
178+
}
179+
180+
Ok(())
181+
})?;
182+
183+
Ok(config)
184+
}
185+
159186
pub(crate) fn config_for_variant(variant: &syn::Variant) -> Result<VariantConfig, syn::Error> {
160187
let mut config = VariantConfig::default();
161188

macros/src/config/tests.rs

Lines changed: 43 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -128,71 +128,69 @@ mod enum_config {
128128

129129
let config = config_for_enum(&item)?;
130130

131-
assert_eq!(config.exclude, None);
132-
133131
Ok(())
134132
}
135133

136134
#[test]
137-
fn accepts_empty_exclude_attrs() -> Result<(), syn::Error> {
135+
fn rejects_unrecognized_exclude_attrs() -> Result<(), syn::Error> {
138136
let item: syn::ItemEnum = parse_quote! {
139-
#[enumcapsulate(exclude)]
137+
#[enumcapsulate(exclude(IntoVariant, Unrecognized))]
140138
enum Dummy {}
141139
};
142140

143-
let config = config_for_enum(&item)?;
144-
145-
let actual: Vec<syn::Ident> = config.exclude.unwrap().idents;
146-
let expected: Vec<syn::Ident> = vec![];
141+
let error = config_for_enum(&item).err().unwrap();
147142

148-
assert_eq!(actual, expected);
143+
assert_eq!(error.to_string(), "unrecognized macro derive");
149144

150145
Ok(())
151146
}
152147

153-
#[test]
154-
fn accepts_non_empty_exclude_attrs() -> Result<(), syn::Error> {
155-
let item: syn::ItemEnum = parse_quote! {
156-
#[enumcapsulate(exclude(AsVariant, IntoVariant))]
157-
enum Dummy {}
158-
};
148+
mod encapsulate {
149+
use super::*;
159150

160-
let config = config_for_enum(&item)?;
151+
#[test]
152+
#[should_panic]
153+
fn rejects_empty_exclude_attrs() -> Result<(), syn::Error> {
154+
let item: syn::ItemEnum = parse_quote! {
155+
#[enumcapsulate(exclude)]
156+
enum Dummy {}
157+
};
161158

162-
let actual = config.exclude.unwrap().idents;
163-
let expected: Vec<syn::Ident> =
164-
vec![parse_quote! { AsVariant }, parse_quote! { IntoVariant }];
159+
let config = encapsulate_config_for_enum(&item)?;
165160

166-
assert_eq!(actual, expected);
161+
Ok(())
162+
}
167163

168-
Ok(())
169-
}
164+
#[test]
165+
fn accepts_non_empty_exclude_attrs() -> Result<(), syn::Error> {
166+
let item: syn::ItemEnum = parse_quote! {
167+
#[enumcapsulate(exclude(AsVariant, IntoVariant))]
168+
enum Dummy {}
169+
};
170170

171-
#[test]
172-
fn rejects_unrecognized_exclude_attrs() -> Result<(), syn::Error> {
173-
let item: syn::ItemEnum = parse_quote! {
174-
#[enumcapsulate(exclude(IntoVariant, Unrecognized))]
175-
enum Dummy {}
176-
};
171+
let config = config_for_enum(&item)?;
177172

178-
let error = config_for_enum(&item).err().unwrap();
173+
let actual = config.exclude.unwrap().idents;
174+
let expected: Vec<syn::Ident> =
175+
vec![parse_quote! { AsVariant }, parse_quote! { IntoVariant }];
179176

180-
assert_eq!(error.to_string(), "unrecognized macro derive");
177+
assert_eq!(actual, expected);
181178

182-
Ok(())
183-
}
179+
Ok(())
180+
}
184181

185-
#[test]
186-
fn is_excluded() {
187-
let config = EnumConfig {
188-
exclude: Some(MacroSelectionConfig {
189-
idents: vec![parse_quote! { FromVariant }, parse_quote! { IntoVariant }],
190-
}),
191-
};
182+
#[test]
183+
fn is_excluded() {
184+
let config = EncapsulateEnumConfig {
185+
exclude: Some(MacroSelectionConfig {
186+
idents: vec![parse_quote! { FromVariant }, parse_quote! { IntoVariant }],
187+
}),
188+
};
192189

193-
assert_eq!(config.is_excluded("FromVariant"), true);
194-
assert_eq!(config.is_excluded("IntoVariant"), true);
195-
assert_eq!(config.is_excluded("AsVariant"), false);
190+
assert_eq!(config.is_excluded("FromVariant"), true);
191+
assert_eq!(config.is_excluded("IntoVariant"), true);
192+
assert_eq!(config.is_excluded("AsVariant"), false);
193+
}
196194
}
197195
}
198196

@@ -416,84 +414,8 @@ mod variant_config {
416414
mod is_excluded {
417415
use super::*;
418416

419-
#[test]
420-
fn no_enum_excludes() {
421-
let enum_config = EnumConfig { exclude: None };
422-
423-
let config = VariantConfig {
424-
exclude: None,
425-
include: None,
426-
field: None,
427-
};
428-
429-
assert_eq!(config.is_excluded("FromVariant", &enum_config), false);
430-
assert_eq!(config.is_excluded("IntoVariant", &enum_config), false);
431-
assert_eq!(config.is_excluded("AsVariant", &enum_config), false);
432-
}
433-
434-
#[test]
435-
fn only_enum_excludes() {
436-
let enum_config = EnumConfig {
437-
exclude: Some(MacroSelectionConfig {
438-
idents: vec![parse_quote! { AsVariant }],
439-
}),
440-
};
441-
442-
let config = VariantConfig {
443-
exclude: None,
444-
include: None,
445-
field: None,
446-
};
447-
448-
assert_eq!(config.is_excluded("FromVariant", &enum_config), false);
449-
assert_eq!(config.is_excluded("IntoVariant", &enum_config), false);
450-
assert_eq!(config.is_excluded("AsVariant", &enum_config), true);
451-
}
452-
453-
#[test]
454-
fn blanket_overridden_enum_excludes() {
455-
let enum_config = EnumConfig {
456-
exclude: Some(MacroSelectionConfig {
457-
idents: vec![parse_quote! { AsVariant }],
458-
}),
459-
};
460-
461-
let config = VariantConfig {
462-
exclude: None,
463-
include: Some(MacroSelectionConfig { idents: vec![] }),
464-
field: None,
465-
};
466-
467-
assert_eq!(config.is_excluded("FromVariant", &enum_config), false);
468-
assert_eq!(config.is_excluded("IntoVariant", &enum_config), false);
469-
assert_eq!(config.is_excluded("AsVariant", &enum_config), false);
470-
}
471-
472-
#[test]
473-
fn selective_overridden_enum_excludes() {
474-
let enum_config = EnumConfig {
475-
exclude: Some(MacroSelectionConfig {
476-
idents: vec![parse_quote! { AsVariant }, parse_quote! { IntoVariant }],
477-
}),
478-
};
479-
480-
let config = VariantConfig {
481-
exclude: None,
482-
include: Some(MacroSelectionConfig {
483-
idents: vec![parse_quote! { AsVariant }],
484-
}),
485-
field: None,
486-
};
487-
488-
assert_eq!(config.is_excluded("FromVariant", &enum_config), false);
489-
assert_eq!(config.is_excluded("IntoVariant", &enum_config), true);
490-
assert_eq!(config.is_excluded("AsVariant", &enum_config), false);
491-
}
492-
493417
#[test]
494418
fn selective_overridden_variant_excludes() {
495-
let enum_config = EnumConfig { exclude: None };
496-
497419
let config = VariantConfig {
498420
exclude: Some(MacroSelectionConfig { idents: vec![] }),
499421
include: Some(MacroSelectionConfig {
@@ -502,9 +424,9 @@ mod variant_config {
502424
field: None,
503425
};
504426

505-
assert_eq!(config.is_excluded("FromVariant", &enum_config), true);
506-
assert_eq!(config.is_excluded("IntoVariant", &enum_config), true);
507-
assert_eq!(config.is_excluded("AsVariant", &enum_config), false);
427+
assert_eq!(config.is_excluded("FromVariant"), true);
428+
assert_eq!(config.is_excluded("IntoVariant"), true);
429+
assert_eq!(config.is_excluded("AsVariant"), false);
508430
}
509431
}
510432
}

0 commit comments

Comments
 (0)