Cleanup renaming

(this technically breaks `#[rename = "actual_name"]` but we wanted to
split `rename` VS `rename_rule` anyway, so this is ok)
This commit is contained in:
Maybe Waffle 2022-08-22 17:36:36 +04:00
parent 88449531a6
commit ddacee966e
4 changed files with 131 additions and 101 deletions

View file

@ -2,7 +2,7 @@ use crate::{
attr::{Attr, BotCommandAttribute},
command_enum::CommandEnum,
fields_parse::ParserType,
rename_rules::rename_by_rule,
rename_rules::RenameRule,
Result,
};
@ -11,24 +11,22 @@ pub(crate) struct Command {
pub description: Option<String>,
pub parser: Option<ParserType>,
pub name: String,
pub renamed: bool,
}
impl Command {
pub fn try_from(attrs: &[Attr], name: &str) -> Result<Self> {
let attrs = parse_attrs(attrs)?;
let mut new_name = name.to_string();
let mut renamed = false;
let CommandAttrs {
prefix,
description,
rename_rule,
parser,
separator: _,
} = attrs;
let prefix = attrs.prefix;
let description = attrs.description;
let rename = attrs.rename;
let parser = attrs.parser;
if let Some(rename_rule) = rename {
new_name = rename_by_rule(name, &rename_rule);
renamed = true;
}
Ok(Self { prefix, description, parser, name: new_name, renamed })
let name = rename_rule.apply(name);
Ok(Self { prefix, description, parser, name })
}
pub fn get_matched_value(&self, global_parameters: &CommandEnum) -> String {
@ -39,11 +37,8 @@ impl Command {
} else {
"/"
};
if let Some(rule) = &global_parameters.rename_rule {
String::from(prefix) + &rename_by_rule(&self.name, rule.as_str())
} else {
String::from(prefix) + &self.name
}
String::from(prefix) + &global_parameters.rename_rule.apply(&self.name)
}
pub fn get_matched_value2(
@ -57,18 +52,15 @@ impl Command {
} else {
"/"
};
if let Some(rule) = &global_parameters.rename_rule {
(String::from(prefix), rename_by_rule(&self.name, rule.as_str()))
} else {
(String::from(prefix), self.name.clone())
}
(String::from(prefix), global_parameters.rename_rule.apply(&self.name))
}
}
pub(crate) struct CommandAttrs {
pub prefix: Option<String>,
pub description: Option<String>,
pub rename: Option<String>,
pub rename_rule: RenameRule,
pub parser: Option<ParserType>,
pub separator: Option<String>,
}
@ -76,7 +68,7 @@ pub(crate) struct CommandAttrs {
pub(crate) fn parse_attrs(attrs: &[Attr]) -> Result<CommandAttrs> {
let mut prefix = None;
let mut description = None;
let mut rename_rule = None;
let mut rename_rule = RenameRule::Identity;
let mut parser = None;
let mut separator = None;
@ -86,7 +78,9 @@ pub(crate) fn parse_attrs(attrs: &[Attr]) -> Result<CommandAttrs> {
BotCommandAttribute::Description => {
description = Some(attr.value())
}
BotCommandAttribute::RenameRule => rename_rule = Some(attr.value()),
BotCommandAttribute::RenameRule => {
rename_rule = RenameRule::parse(&attr.value())?
}
BotCommandAttribute::CustomParser => {
parser = Some(ParserType::parse(&attr.value()))
}
@ -94,11 +88,5 @@ pub(crate) fn parse_attrs(attrs: &[Attr]) -> Result<CommandAttrs> {
}
}
Ok(CommandAttrs {
prefix,
description,
rename: rename_rule,
parser,
separator,
})
Ok(CommandAttrs { prefix, description, rename_rule, parser, separator })
}

View file

@ -1,13 +1,13 @@
use crate::{
attr::Attr, command::parse_attrs, error::compile_error,
fields_parse::ParserType, Result,
attr::Attr, command::parse_attrs, fields_parse::ParserType,
rename_rules::RenameRule, Result,
};
#[derive(Debug)]
pub(crate) struct CommandEnum {
pub prefix: Option<String>,
pub description: Option<String>,
pub rename_rule: Option<String>,
pub rename_rule: RenameRule,
pub parser_type: ParserType,
}
@ -17,7 +17,7 @@ impl CommandEnum {
let prefix = attrs.prefix;
let description = attrs.description;
let rename = attrs.rename;
let rename = attrs.rename_rule;
let separator = attrs.separator;
let mut parser = attrs.parser.unwrap_or(ParserType::Default);
if let (ParserType::Split { separator }, Some(s)) =
@ -25,19 +25,6 @@ impl CommandEnum {
{
*separator = Some(s.clone())
}
if let Some(rename_rule) = &rename {
match rename_rule.as_str() {
"lowercase"
| "UPPERCASE"
| "PascalCase"
| "camelCase"
| "snake_case"
| "SCREAMING_SNAKE_CASE"
| "kebab-case"
| "SCREAMING-KEBAB-CASE" => {}
_ => return Err(compile_error("disallowed value")),
}
}
Ok(Self {
prefix,
description,

View file

@ -3,6 +3,7 @@ use quote::{quote, ToTokens};
pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug)]
pub(crate) struct Error(TokenStream);
pub(crate) fn compile_error<T>(data: T) -> Error

View file

@ -5,23 +5,75 @@ use heck::{
ToShoutySnakeCase, ToSnakeCase,
};
/// Apply a renaming rule to an enum variant,
/// returning the version expected in the source.
///
/// The possible `rule` can be: `lowercase`, `UPPERCASE`, `PascalCase`,
/// `camelCase`, `snake_case`, `SCREAMING_SNAKE_CASE`, `kebab-case`,
/// `SCREAMING-KEBAB-CASE`. See tests for the details how it will work.
pub(crate) fn rename_by_rule(input: &str, rule: &str) -> String {
match rule {
"lowercase" => input.to_lowercase(),
"UPPERCASE" => input.to_uppercase(),
"PascalCase" => input.to_pascal_case(),
"camelCase" => input.to_lower_camel_case(),
"snake_case" => input.to_snake_case(),
"SCREAMING_SNAKE_CASE" => input.to_shouty_snake_case(),
"kebab-case" => input.to_kebab_case(),
"SCREAMING-KEBAB-CASE" => input.to_shouty_kebab_case(),
_ => rule.to_string(),
use crate::error::{compile_error, Result};
#[derive(Copy, Clone, Debug)]
pub(crate) enum RenameRule {
/// -> `lowercase`
LowerCase,
/// -> `UPPERCASE`
UpperCase,
/// -> `PascalCase`
PascalCase,
/// -> `camelCase`
CamelCase,
/// -> `snake_case`
SnakeCase,
/// -> `SCREAMING_SNAKE_CASE`
ScreamingSnakeCase,
/// -> `kebab-case`
KebabCase,
/// -> `SCREAMING-KEBAB-CASE`
ScreamingKebabCase,
/// Leaves input as-is
Identity,
}
impl RenameRule {
/// Apply a renaming rule to a string, returning the version expected in the
/// source.
///
/// See tests for the details how it will work.
pub fn apply(self, input: &str) -> String {
use RenameRule::*;
match self {
LowerCase => input.to_lowercase(),
UpperCase => input.to_uppercase(),
PascalCase => input.to_pascal_case(),
CamelCase => input.to_lower_camel_case(),
SnakeCase => input.to_snake_case(),
ScreamingSnakeCase => input.to_shouty_snake_case(),
KebabCase => input.to_kebab_case(),
ScreamingKebabCase => input.to_shouty_kebab_case(),
Identity => input.to_owned(),
}
}
pub fn parse(rule: &str) -> Result<Self> {
use RenameRule::*;
let rule = match rule {
"lowercase" => LowerCase,
"UPPERCASE" => UpperCase,
"PascalCase" => PascalCase,
"camelCase" => CamelCase,
"snake_case" => SnakeCase,
"SCREAMING_SNAKE_CASE" => ScreamingSnakeCase,
"kebab-case" => KebabCase,
"SCREAMING-KEBAB-CASE" => ScreamingKebabCase,
"identity" => Identity,
invalid => {
return Err(compile_error(format!(
"invalid rename rule `{invalid}` (supported rules: \
`lowercase`, `UPPERCASE`, `PascalCase`, `camelCase`, \
`snake_case`, `SCREAMING_SNAKE_CASE`, `kebab-case`, \
`SCREAMING-KEBAB-CASE` and `identity`)"
)))
}
};
Ok(rule)
}
}
@ -30,8 +82,10 @@ mod tests {
use super::*;
macro_rules! test_eq {
($lval:expr, $rval:expr) => {
assert_eq!(rename_by_rule($lval, TYPE), $rval);
($lval:expr => $rval:expr) => {
let rule = RenameRule::parse(TYPE).unwrap();
assert_eq!(rule.apply($lval), $rval);
};
}
@ -39,79 +93,79 @@ mod tests {
fn test_lowercase() {
const TYPE: &str = "lowercase";
test_eq!("HelloWorld", "helloworld");
test_eq!("Hello_World", "hello_world");
test_eq!("Hello-World", "hello-world");
test_eq!("helloWorld", "helloworld");
test_eq!("HelloWorld" => "helloworld");
test_eq!("Hello_World" => "hello_world");
test_eq!("Hello-World" => "hello-world");
test_eq!("helloWorld" => "helloworld");
}
#[test]
fn test_uppercase() {
const TYPE: &str = "UPPERCASE";
test_eq!("HelloWorld", "HELLOWORLD");
test_eq!("Hello_World", "HELLO_WORLD");
test_eq!("Hello-World", "HELLO-WORLD");
test_eq!("helloWorld", "HELLOWORLD");
test_eq!("HelloWorld" => "HELLOWORLD");
test_eq!("Hello_World" => "HELLO_WORLD");
test_eq!("Hello-World" => "HELLO-WORLD");
test_eq!("helloWorld" => "HELLOWORLD");
}
#[test]
fn test_pascalcase() {
const TYPE: &str = "PascalCase";
test_eq!("HelloWorld", "HelloWorld");
test_eq!("Hello_World", "HelloWorld");
test_eq!("Hello-World", "HelloWorld");
test_eq!("helloWorld", "HelloWorld");
test_eq!("HelloWorld" => "HelloWorld");
test_eq!("Hello_World" => "HelloWorld");
test_eq!("Hello-World" => "HelloWorld");
test_eq!("helloWorld" => "HelloWorld");
}
#[test]
fn test_camelcase() {
const TYPE: &str = "camelCase";
test_eq!("HelloWorld", "helloWorld");
test_eq!("Hello_World", "helloWorld");
test_eq!("Hello-World", "helloWorld");
test_eq!("helloWorld", "helloWorld");
test_eq!("HelloWorld" => "helloWorld");
test_eq!("Hello_World" => "helloWorld");
test_eq!("Hello-World" => "helloWorld");
test_eq!("helloWorld" => "helloWorld");
}
#[test]
fn test_snakecase() {
const TYPE: &str = "snake_case";
test_eq!("HelloWorld", "hello_world");
test_eq!("Hello_World", "hello_world");
test_eq!("Hello-World", "hello_world");
test_eq!("helloWorld", "hello_world");
test_eq!("HelloWorld" => "hello_world");
test_eq!("Hello_World" => "hello_world");
test_eq!("Hello-World" => "hello_world");
test_eq!("helloWorld" => "hello_world");
}
#[test]
fn test_screaming_snakecase() {
const TYPE: &str = "SCREAMING_SNAKE_CASE";
test_eq!("HelloWorld", "HELLO_WORLD");
test_eq!("Hello_World", "HELLO_WORLD");
test_eq!("Hello-World", "HELLO_WORLD");
test_eq!("helloWorld", "HELLO_WORLD");
test_eq!("HelloWorld" => "HELLO_WORLD");
test_eq!("Hello_World" => "HELLO_WORLD");
test_eq!("Hello-World" => "HELLO_WORLD");
test_eq!("helloWorld" => "HELLO_WORLD");
}
#[test]
fn test_kebabcase() {
const TYPE: &str = "kebab-case";
test_eq!("HelloWorld", "hello-world");
test_eq!("Hello_World", "hello-world");
test_eq!("Hello-World", "hello-world");
test_eq!("helloWorld", "hello-world");
test_eq!("HelloWorld" => "hello-world");
test_eq!("Hello_World" => "hello-world");
test_eq!("Hello-World" => "hello-world");
test_eq!("helloWorld" => "hello-world");
}
#[test]
fn test_screaming_kebabcase() {
const TYPE: &str = "SCREAMING-KEBAB-CASE";
test_eq!("HelloWorld", "HELLO-WORLD");
test_eq!("Hello_World", "HELLO-WORLD");
test_eq!("Hello-World", "HELLO-WORLD");
test_eq!("helloWorld", "HELLO-WORLD");
test_eq!("HelloWorld" => "HELLO-WORLD");
test_eq!("Hello_World" => "HELLO-WORLD");
test_eq!("Hello-World" => "HELLO-WORLD");
test_eq!("helloWorld" => "HELLO-WORLD");
}
}