Skip to main content

slug_preserve/
case.rs

1//! Case-mode handling.
2
3/// How to handle character case in the slugified output.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum CaseMode {
6    /// Keep the original case of each character.
7    Preserve,
8    /// Lowercase the entire string.
9    Lower,
10    /// Uppercase the entire string.
11    Upper,
12    /// Title-case: first char of each word uppercase, rest lowercase. Word
13    /// boundaries are non-alphanumeric chars and start of string. This
14    /// matches the *effective* output of the Python `slugify_camel_iso`
15    /// pipeline (`.capitalize()` followed by `_[a-z] → _X`).
16    Title,
17    /// Synonym for [`CaseMode::Title`] kept for clarity in CLI flag values
18    /// (`--case=capitalize`).
19    Capitalize,
20}
21
22/// Apply a case mode to a string.
23#[must_use]
24pub fn apply(input: &str, mode: CaseMode) -> String {
25    match mode {
26        CaseMode::Preserve => input.to_string(),
27        CaseMode::Lower => input.to_lowercase(),
28        CaseMode::Upper => input.to_uppercase(),
29        CaseMode::Title | CaseMode::Capitalize => title_case(input),
30    }
31}
32
33fn title_case(input: &str) -> String {
34    let mut out = String::with_capacity(input.len());
35    let mut at_word_start = true;
36    for c in input.chars() {
37        if !c.is_ascii_alphanumeric() {
38            out.push(c);
39            at_word_start = true;
40            continue;
41        }
42        if at_word_start {
43            for u in c.to_uppercase() {
44                out.push(u);
45            }
46            at_word_start = false;
47        } else {
48            for l in c.to_lowercase() {
49                out.push(l);
50            }
51        }
52    }
53    out
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn preserve_unchanged() {
62        assert_eq!(apply("Hello-World", CaseMode::Preserve), "Hello-World");
63    }
64
65    #[test]
66    fn lower_lowercases() {
67        assert_eq!(apply("Hello-World", CaseMode::Lower), "hello-world");
68    }
69
70    #[test]
71    fn upper_uppercases() {
72        assert_eq!(apply("Hello-World", CaseMode::Upper), "HELLO-WORLD");
73    }
74
75    #[test]
76    fn title_capitalizes_each_word() {
77        assert_eq!(apply("hello-world", CaseMode::Title), "Hello-World");
78    }
79
80    #[test]
81    fn capitalize_same_as_title() {
82        assert_eq!(
83            apply("hello-world", CaseMode::Capitalize),
84            apply("hello-world", CaseMode::Title)
85        );
86    }
87
88    #[test]
89    fn title_case_lowercases_non_first_chars() {
90        assert_eq!(apply("HELLO WORLD", CaseMode::Title), "Hello World");
91    }
92}