beans/
case.rs

1//! # Case
2//!
3//! This module contains primitives about case.
4//! It provides an interface to compute the case of a given string.
5
6#[cfg(test)]
7mod tests {
8    use super::*;
9    #[test]
10    fn case_recogniser() {
11        use Case::*;
12        assert_eq!(Case::compute("snakecase"), SnakeCase);
13        assert_eq!(Case::compute("snake_case"), SnakeCase);
14        assert_eq!(Case::compute("PascalCase"), PascalCase);
15        assert_eq!(Case::compute("camelCase"), CamelCase);
16        assert_eq!(Case::compute("Pascal_Snake_Case"), PascalSnakeCase);
17        assert_eq!(Case::compute("UPPERCASE"), UpperCase);
18        assert_eq!(Case::compute("SCREAMING_SNAKE_CASE"), ScreamingSnakeCase);
19        assert_eq!(Case::compute(""), Other);
20        assert_eq!(Case::compute("__"), Other);
21        assert_eq!(Case::compute("fake snake case"), Other);
22    }
23}
24
25/// # Summary
26///
27/// Indicate which type of case a certain string is written in.
28#[derive(Debug, PartialEq, Eq)]
29#[allow(clippy::enum_variant_names)]
30pub enum Case {
31    /// snakecase, snake_case
32    SnakeCase,
33    /// PascalCase
34    PascalCase,
35    /// camelCase,
36    CamelCase,
37    /// Pascal_Snake_Case,
38    PascalSnakeCase,
39    /// UPPERCASE
40    UpperCase,
41    /// SCREAMING_SNAKE_CASE,
42    ScreamingSnakeCase,
43    /// Not a known case
44    Other,
45}
46
47impl Case {
48    pub fn compute(string: &str) -> Self {
49        if string.is_empty() {
50            return Self::Other;
51        }
52
53        let mut contains_underscore = false;
54        let mut first_is_capital = false;
55        let mut contains_small = false;
56        let mut contains_capital = false;
57
58        let mut contains_non_alphanumeric = false;
59
60        let mut first_alpha = true;
61
62        for c in string.chars() {
63            if c == '_' {
64                contains_underscore = true;
65            } else if c.is_ascii_uppercase() {
66                contains_capital = true;
67                if first_alpha {
68                    first_is_capital = true;
69                }
70                first_alpha = false;
71            } else if c.is_ascii_lowercase() {
72                contains_small = true;
73                first_alpha = false;
74            } else {
75                contains_non_alphanumeric = true;
76            }
77        }
78
79        if first_alpha || contains_non_alphanumeric {
80            Self::Other
81        } else if !contains_capital {
82            Self::SnakeCase
83        } else if !first_is_capital {
84            Self::CamelCase
85        } else if contains_small {
86            if contains_underscore {
87                Self::PascalSnakeCase
88            } else {
89                Self::PascalCase
90            }
91        } else if contains_underscore {
92            Self::ScreamingSnakeCase
93        } else {
94            Self::UpperCase
95        }
96    }
97}
98
99pub trait CaseProcess {
100    fn case(&self) -> Case;
101}
102
103impl CaseProcess for &str {
104    fn case(&self) -> Case {
105        Case::compute(self)
106    }
107}
108
109impl CaseProcess for String {
110    fn case(&self) -> Case {
111        Case::compute(self.as_str())
112    }
113}