Skip to content

Commit

Permalink
tb: fix case check logic
Browse files Browse the repository at this point in the history
This commit fixes an issue with `convert_case` crate reported in [1].

The issue is that the default implementation of `is_case` provided by
that crate initializes the converter with almost all available
boundaries.

As a result the following conversion takes place:

"ERR1".to_case(Case::UpperSnake) -> "ERR_1"

Furthermore, the following check fails, while it seems like it
shouldn't because the string is in capital letters.

"ERR1".is_case(Case::UpperSnake)

This commit introduces a new primitive `convert_case` which receives two
case parameters, one for the source case and one of the target case.
This way the converter is initialized with the correct source case
boundaries. And it allows implementation of a new `is_case` primitive
that builds on `convert_case` to select the source case boundaries
correctly.

[1] rutrum/convert-case#21
  • Loading branch information
r-bk committed Jul 19, 2024
1 parent c5c9e51 commit fbfc8bf
Showing 1 changed file with 36 additions and 4 deletions.
40 changes: 36 additions & 4 deletions crates/tighterror-build/src/parser/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
errors::{kinds::parser::*, TbError},
parser::kws,
};
use convert_case::{Case, Casing};
use convert_case::Case;
use regex::Regex;
use std::collections::HashSet;

Expand All @@ -19,6 +19,17 @@ pub fn check_ident_chars(ident: &str, desc: &str) -> Result<(), TbError> {
}
}

fn convert_case(s: &str, from_case: Case, to_case: Case) -> String {
let converter = convert_case::Converter::new()
.from_case(from_case)
.to_case(to_case);
converter.convert(s)
}

fn is_case(s: &str, case: Case) -> bool {
convert_case(s, case, case) == s
}

fn check_ident(ident: &str, desc: &str, case: Case) -> Result<(), TbError> {
if ident.is_empty() {
log::error!("`{desc}` cannot be an empty string");
Expand All @@ -27,10 +38,10 @@ fn check_ident(ident: &str, desc: &str, case: Case) -> Result<(), TbError> {

check_ident_chars(ident, desc)?;

if !ident.is_case(case) {
if !is_case(ident, case) {
log::error!(
"`{desc}` must be specified in {case:?} case: {ident} -> {}",
ident.to_case(case)
convert_case(ident, case, case)
);
return BAD_IDENTIFIER_CASE.into();
}
Expand Down Expand Up @@ -84,7 +95,7 @@ pub fn check_module_name(name: &str) -> Result<(), TbError> {
if name.is_empty() {
log::error!("module name cannot be an empty string");
BAD_NAME.into()
} else if !name.is_case(Case::Snake) {
} else if !is_case(name, Case::Snake) {
log::error!("module name must be specified in lower_snake_case: {name}");
BAD_NAME.into()
} else {
Expand Down Expand Up @@ -152,3 +163,24 @@ where
{
check_name_uniqueness("module", iter)
}

#[cfg(test)]
mod testing {
use super::*;
use convert_case::Case::*;

#[test]
fn test_convert_case() {
let cases = &[
// str_to_convert, from_case, to_case, expected_result
("MY_ERR1", UpperSnake, UpperSnake, "MY_ERR1"),
("MyErr1", UpperSnake, UpperSnake, "MYERR1"),
("MyErr1", UpperCamel, UpperSnake, "MY_ERR_1"),
("MY_ERR1", UpperSnake, UpperCamel, "MyErr1"),
];

for c in cases {
assert_eq!(convert_case(c.0, c.1, c.2), c.3);
}
}
}

0 comments on commit fbfc8bf

Please sign in to comment.