forked from SeaQL/sea-orm
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: Add Basic Support For Providing String Value For ActiveEnum Vari…
…ants Based On Renaming Rules SeaQL#2160
- Loading branch information
1 parent
e7a4909
commit ab04027
Showing
4 changed files
with
180 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
use std::fmt; | ||
use std::fmt::{Debug, Display}; | ||
use syn::meta::ParseNestedMeta; | ||
use syn::Error; | ||
use syn::LitStr; | ||
use RenameRule::*; | ||
|
||
#[derive(Copy, Clone, PartialEq)] | ||
pub enum RenameRule { | ||
/// Rename direct children to "lowercase" style. | ||
LowerCase, | ||
/// Rename direct children to "UPPERCASE" style. | ||
UpperCase, | ||
/// Rename direct children to "PascalCase" style, as typically used for | ||
/// enum variants. | ||
PascalCase, | ||
/// Rename direct children to "camelCase" style. | ||
CamelCase, | ||
/// Rename direct children to "snake_case" style, as commonly used for | ||
/// fields. | ||
SnakeCase, | ||
/// Rename direct children to "SCREAMING_SNAKE_CASE" style, as commonly | ||
/// used for constants. | ||
ScreamingSnakeCase, | ||
/// Rename direct children to "kebab-case" style. | ||
KebabCase, | ||
/// Rename direct children to "SCREAMING-KEBAB-CASE" style. | ||
ScreamingKebabCase, | ||
} | ||
|
||
static RENAME_RULES: &[(&str, RenameRule)] = &[ | ||
("lowercase", LowerCase), | ||
("UPPERCASE", UpperCase), | ||
("PascalCase", PascalCase), | ||
("camelCase", CamelCase), | ||
("snake_case", SnakeCase), | ||
("SCREAMING_SNAKE_CASE", ScreamingSnakeCase), | ||
("kebab-case", KebabCase), | ||
("SCREAMING-KEBAB-CASE", ScreamingKebabCase), | ||
]; | ||
|
||
impl RenameRule { | ||
pub fn from_str(rename_all_str: &str) -> Result<Self, ParseError> { | ||
for (name, rule) in RENAME_RULES { | ||
if rename_all_str == *name { | ||
return Ok(*rule); | ||
} | ||
} | ||
Err(ParseError { | ||
unknown: rename_all_str, | ||
}) | ||
} | ||
|
||
/// Apply a renaming rule to an enum variant, returning the version expected in the source. | ||
pub fn apply_to_variant(self, variant: &str) -> String { | ||
match self { | ||
PascalCase => variant.to_owned(), | ||
LowerCase => variant.to_ascii_lowercase(), | ||
UpperCase => variant.to_ascii_uppercase(), | ||
CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..], | ||
SnakeCase => { | ||
let mut snake = String::new(); | ||
for (i, ch) in variant.char_indices() { | ||
if i > 0 && ch.is_uppercase() { | ||
snake.push('_'); | ||
} | ||
snake.push(ch.to_ascii_lowercase()); | ||
} | ||
snake | ||
} | ||
ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(), | ||
KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"), | ||
ScreamingKebabCase => ScreamingSnakeCase | ||
.apply_to_variant(variant) | ||
.replace('_', "-"), | ||
} | ||
} | ||
|
||
/// Apply a renaming rule to a struct field, returning the version expected in the source. | ||
pub fn apply_to_field(self, field: &str) -> String { | ||
match self { | ||
LowerCase | SnakeCase => field.to_owned(), | ||
UpperCase => field.to_ascii_uppercase(), | ||
PascalCase => { | ||
let mut pascal = String::new(); | ||
let mut capitalize = true; | ||
for ch in field.chars() { | ||
if ch == '_' { | ||
capitalize = true; | ||
} else if capitalize { | ||
pascal.push(ch.to_ascii_uppercase()); | ||
capitalize = false; | ||
} else { | ||
pascal.push(ch); | ||
} | ||
} | ||
pascal | ||
} | ||
CamelCase => { | ||
let pascal = PascalCase.apply_to_field(field); | ||
pascal[..1].to_ascii_lowercase() + &pascal[1..] | ||
} | ||
ScreamingSnakeCase => field.to_ascii_uppercase(), | ||
KebabCase => field.replace('_', "-"), | ||
ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-"), | ||
} | ||
} | ||
} | ||
|
||
pub struct ParseError<'a> { | ||
unknown: &'a str, | ||
} | ||
|
||
impl<'a> Display for ParseError<'a> { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
f.write_str("unknown rename rule `rename_all = ")?; | ||
Debug::fmt(self.unknown, f)?; | ||
f.write_str("`, expected one of ")?; | ||
for (i, (name, _rule)) in RENAME_RULES.iter().enumerate() { | ||
if i > 0 { | ||
f.write_str(", ")?; | ||
} | ||
Debug::fmt(name, f)?; | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl<'meta> TryFrom<&ParseNestedMeta<'meta>> for RenameRule { | ||
type Error = Error; | ||
|
||
fn try_from(value: &ParseNestedMeta) -> Result<Self, Self::Error> { | ||
let meta_string_literal: LitStr = value.value()?.parse()?; | ||
match RenameRule::from_str(meta_string_literal.value().as_str()) { | ||
Ok(rule) => Ok(rule), | ||
Err(error) => Err(value.error(format!( | ||
"Unknown value for attribute parameter `rename_all`: {error}", | ||
))), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters