use crate::boundary;
use crate::boundary::Boundary;
use crate::pattern::Pattern;
use crate::Case;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
pub struct Converter {
pub boundaries: Vec<Boundary>,
pub patterns: Vec<Pattern>,
pub delimiter: String,
}
impl Default for Converter {
fn default() -> Self {
Converter {
boundaries: Boundary::defaults().to_vec(),
patterns: Vec::new(),
delimiter: String::new(),
}
}
}
impl Converter {
pub fn new() -> Self {
Self::default()
}
pub fn convert<T>(&self, s: T) -> String
where
T: AsRef<str>,
{
let words = boundary::split(&s, &self.boundaries);
let mut result: Vec<String> = words.into_iter().map(|s| s.to_string()).collect();
for pattern in &self.patterns {
result = pattern.mutate(&result);
}
result.join(&self.delimiter)
}
pub fn to_case(mut self, case: Case) -> Self {
self.patterns.push(case.pattern());
self.delimiter = case.delimiter().to_string();
self
}
pub fn from_case(mut self, case: Case) -> Self {
self.boundaries = case.boundaries().to_vec();
self
}
pub fn set_boundaries(mut self, bs: &[Boundary]) -> Self {
self.boundaries = bs.to_vec();
self
}
pub fn add_boundary(mut self, b: Boundary) -> Self {
self.boundaries.push(b);
self
}
pub fn add_boundaries(mut self, bs: &[Boundary]) -> Self {
self.boundaries.extend(bs);
self
}
pub fn remove_boundary(mut self, b: Boundary) -> Self {
self.boundaries.retain(|&x| x != b);
self
}
pub fn remove_boundaries(mut self, bs: &[Boundary]) -> Self {
for b in bs {
self.boundaries.retain(|&x| x != *b);
}
self
}
pub fn set_pattern(mut self, p: Pattern) -> Self {
self.patterns = vec![p];
self
}
pub fn set_patterns(mut self, ps: &[Pattern]) -> Self {
self.patterns = ps.to_vec();
self
}
pub fn add_pattern(mut self, p: Pattern) -> Self {
self.patterns.push(p);
self
}
pub fn add_patterns(mut self, ps: &[Pattern]) -> Self {
self.patterns.extend(ps);
self
}
pub fn remove_pattern(mut self, p: Pattern) -> Self {
self.patterns.retain(|&x| x != p);
self
}
pub fn remove_patterns(mut self, ps: &[Pattern]) -> Self {
for p in ps {
self.patterns.retain(|&x| x != *p);
}
self
}
pub fn set_delimiter<T>(mut self, d: T) -> Self
where
T: ToString,
{
self.delimiter = d.to_string();
self
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::Casing;
#[test]
fn snake_converter_from_case() {
let conv = Converter::new().to_case(Case::Snake);
let s = String::from("my var name");
assert_eq!(s.to_case(Case::Snake), conv.convert(s));
}
#[test]
fn snake_converter_from_scratch() {
let conv = Converter::new()
.set_delimiter("_")
.set_patterns(&[Pattern::Lowercase]);
let s = String::from("my var name");
assert_eq!(s.to_case(Case::Snake), conv.convert(s));
}
#[test]
fn custom_pattern() {
let conv = Converter::new()
.to_case(Case::Snake)
.set_patterns(&[Pattern::Sentence]);
assert_eq!(conv.convert("bjarne case"), "Bjarne_case");
}
#[test]
fn custom_delim() {
let conv = Converter::new().set_delimiter("..");
assert_eq!(conv.convert("ohMy"), "oh..My");
}
#[test]
fn no_delim() {
let conv = Converter::new()
.from_case(Case::Title)
.to_case(Case::Kebab)
.set_delimiter("");
assert_eq!(conv.convert("Just Flat"), "justflat");
}
#[test]
fn no_digit_boundaries() {
let conv = Converter::new()
.remove_boundaries(&Boundary::digits())
.to_case(Case::Snake);
assert_eq!(conv.convert("Test 08Bound"), "test_08bound");
assert_eq!(conv.convert("a8aA8A"), "a8a_a8a");
}
#[test]
fn remove_boundary() {
let conv = Converter::new()
.remove_boundary(Boundary::DigitUpper)
.to_case(Case::Snake);
assert_eq!(conv.convert("Test 08Bound"), "test_08bound");
assert_eq!(conv.convert("a8aA8A"), "a_8_a_a_8a");
}
#[test]
fn add_boundary() {
let conv = Converter::new()
.from_case(Case::Snake)
.to_case(Case::Kebab)
.add_boundary(Boundary::LowerUpper);
assert_eq!(conv.convert("word_wordWord"), "word-word-word");
}
#[test]
fn add_boundaries() {
let conv = Converter::new()
.from_case(Case::Snake)
.to_case(Case::Kebab)
.add_boundaries(&[Boundary::LowerUpper, Boundary::UpperLower]);
assert_eq!(conv.convert("word_wordWord"), "word-word-w-ord");
}
#[test]
fn twice() {
let s = "myVarName".to_string();
let conv = Converter::new().to_case(Case::Snake);
let snake = conv.convert(&s);
let kebab = s.to_case(Case::Kebab);
assert_eq!(snake.to_case(Case::Camel), kebab.to_case(Case::Camel));
}
#[test]
fn reuse_after_change() {
let conv = Converter::new().from_case(Case::Snake).to_case(Case::Kebab);
assert_eq!(conv.convert("word_wordWord"), "word-wordword");
let conv = conv.add_boundary(Boundary::LowerUpper);
assert_eq!(conv.convert("word_wordWord"), "word-word-word");
}
#[test]
fn explicit_boundaries() {
let conv = Converter::new()
.set_boundaries(&[
Boundary::DigitLower,
Boundary::DigitUpper,
Boundary::Acronym,
])
.to_case(Case::Snake);
assert_eq!(
conv.convert("section8lesson2HTTPRequests"),
"section8_lesson2_http_requests"
);
}
}