Skip to content

Commit

Permalink
new: Support before_parse for ConfigEnum. (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
milesj authored Aug 23, 2023
1 parent df0faae commit 424aaac
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 19 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
#### 🚀 Updates

- Added `type_semver` feature, that implements schematic types for the `semver` crate.
- Added basic `#[config]` support for `ConfigEnum`.
- Supports `rename` and `rename_all` (matches serde).
- Added `before_parse` which transforms the string value before parsing (`From`, `FromStr`, etc).
Supports "lowercase" and "UPPERCASE".

#### 🐞 Fixes

- Fixed serde `rename` not working on `ConfigEnum`.

## 0.11.2

Expand Down
2 changes: 2 additions & 0 deletions crates/config/tests/macros_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ enum BasicEnum {

#[derive(ConfigEnum, Debug, Deserialize, Serialize)]
#[serde(rename = "Test", rename_all = "UPPERCASE")]
#[config(before_parse = "UPPERCASE")]
enum CustomFormatEnum {
Foo,
#[serde(rename = "bAr")]
Expand All @@ -252,6 +253,7 @@ enum OtherEnum {
}

#[derive(ConfigEnum, Debug, Serialize)]
#[config(rename = "Aliased", before_parse = "lowercase")]
enum AliasedEnum {
#[serde(alias = "a")]
Foo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ expression: "std::fs::read_to_string(file).unwrap()"
},
"additionalProperties": false,
"definitions": {
"AliasedEnum": {
"Aliased": {
"type": "string",
"enum": [
"foo",
Expand Down Expand Up @@ -106,14 +106,6 @@ expression: "std::fs::read_to_string(file).unwrap()"
},
"additionalProperties": false
},
"CustomFormatEnum": {
"type": "string",
"enum": [
"FOO",
"bAr",
"b-a-z"
]
},
"DefaultValues": {
"title": "DefaultValues",
"type": "object",
Expand Down Expand Up @@ -734,6 +726,14 @@ expression: "std::fs::read_to_string(file).unwrap()"
"c"
]
},
"Test": {
"type": "string",
"enum": [
"FOO",
"bAr",
"b-a-z"
]
},
"Validations": {
"title": "Validations",
"type": "object",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const enum BasicEnum {
Baz,
}

export const enum CustomFormatEnum {
export const enum Test {
Foo,
Bar,
Baz,
Expand All @@ -32,7 +32,7 @@ export const enum OtherEnum {
Other,
}

export const enum AliasedEnum {
export const enum Aliased {
Foo,
Bar,
Baz,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ export type SomeEnum = 'a' | 'b' | 'c';

export type BasicEnum = 'foo' | 'bar' | 'baz';

export type CustomFormatEnum = 'FOO' | 'bAr' | 'b-a-z';
export type Test = 'FOO' | 'bAr' | 'b-a-z';

export type OtherEnum = 'foo' | 'bar' | 'baz';

export type AliasedEnum = 'foo' | 'bar' | 'baz';
export type Aliased = 'foo' | 'bar' | 'baz';

export interface ValueTypes {
boolean: boolean;
Expand Down
52 changes: 46 additions & 6 deletions crates/macros/src/config_enum/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,50 @@ use syn::{parse_macro_input, Data, DeriveInput};
supports(enum_unit, enum_tuple)
)]
pub struct SerdeArgs {
rename: Option<String>,
rename_all: Option<String>,
}

// #[config()]
#[derive(FromDeriveInput, Default)]
#[darling(default, attributes(config), supports(enum_unit, enum_tuple))]
pub struct ConfigEnumArgs {
before_parse: Option<String>,

// serde
rename: Option<String>,
rename_all: Option<String>,
}

// #[derive(ConfigEnum)]
pub fn macro_impl(item: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(item);
let args = ConfigEnumArgs::from_derive_input(&input).expect("Failed to parse arguments.");
let serde_args = SerdeArgs::from_derive_input(&input).unwrap_or_default();

let Data::Enum(data) = input.data else {
panic!("Only unit enums are supported.");
};

let enum_name = &input.ident;
let meta_name = enum_name.to_string();
let case_format = serde_args
let meta_name = args
.rename
.as_deref()
.or(serde_args.rename.as_deref())
.map(|n| n.to_owned())
.unwrap_or(enum_name.to_string());

let casing_format = args
.rename_all
.unwrap_or_else(|| "kebab-case".to_owned());
.as_deref()
.or(serde_args.rename_all.as_deref())
.unwrap_or("kebab-case");

// Extract unit variants
let variants = data
.variants
.iter()
.map(|v| Variant::from(v, &case_format))
.map(|v| Variant::from(v, casing_format))
.collect::<Vec<_>>();

// Render variants to tokens
Expand All @@ -62,6 +83,24 @@ pub fn macro_impl(item: TokenStream) -> TokenStream {
}
}

let before_parse = if let Some(parser) = &args.before_parse {
if parser == "lowercase" {
quote! {
let value = value.to_lowercase();
let value = value.as_str();
}
} else if parser == "UPPERCASE" {
quote! {
let value = value.to_uppercase();
let value = value.as_str();
}
} else {
panic!("Unknown `before_parse` value {}", parser);
}
} else {
quote! {}
};

let from_fallback = if has_fallback {
quote! {}
} else {
Expand Down Expand Up @@ -90,8 +129,9 @@ pub fn macro_impl(item: TokenStream) -> TokenStream {
impl std::str::FromStr for #enum_name {
type Err = schematic::ConfigError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
fn from_str(value: &str) -> Result<Self, Self::Err> {
#before_parse
Ok(match value {
#(#from_stmts)*
#from_fallback
})
Expand Down

0 comments on commit 424aaac

Please sign in to comment.