ligen_ir/identifier/
naming_convention.rs1use crate::{prelude::*, Identifier};
4
5#[derive(Debug, Clone, Serialize, Deserialize, Display, PartialEq, Eq)]
7pub enum NamingConvention {
8    KebabCase,
10
11    SnakeCase,
13
14    PascalCase,
16
17    CamelCase,
19
20    Unknown,
22}
23
24impl Identifier {
25    pub fn naming_convention(&self) -> NamingConvention {
27        if self.name.contains('-') {
28            NamingConvention::KebabCase
29        } else if self.name.contains('_') {
30            NamingConvention::SnakeCase
31        } else if self.name.chars().next().unwrap().is_uppercase() {
32            NamingConvention::PascalCase
33        } else if self.name.chars().any(|c| c.is_uppercase()) {
34            NamingConvention::CamelCase
35        } else {
36            NamingConvention::Unknown
37        }
38    }
39
40    pub fn to_camel_case(&self) -> Self {
42        let mut result = String::new();
43        let mut first = true;
44        for word in self.words() {
45            if first {
46                result.push_str(word.to_lowercase().as_str());
47                first = false;
48            } else {
49                result.push_str(&word[..1].to_uppercase());
50                result.push_str(&word[1..]);
51            }
52        }
53        result.into()
54    }
55
56    pub fn to_pascal_case(&self) -> Self {
58        let mut result = String::new();
59        for word in self.words() {
60            result.push_str(&word[..1].to_uppercase());
61            result.push_str(&word[1..]);
62        }
63        result.into()
64    }
65
66    pub fn to_snake_case(&self) -> Self {
68        let mut result = String::new();
69        let mut first = true;
70        for word in self.words() {
71            if first {
72                result.push_str(word.to_lowercase().as_str());
73                first = false;
74            } else {
75                result.push('_');
76                result.push_str(word.to_lowercase().as_str());
77            }
78        }
79        result.into()
80    }
81
82    pub fn to_kebab_case(&self) -> Self {
84        let mut result = String::new();
85        let mut first = true;
86        for word in self.words() {
87            if first {
88                result.push_str(word.to_lowercase().as_str());
89                first = false;
90            } else {
91                result.push('-');
92                result.push_str(word.to_lowercase().as_str());
93            }
94        }
95        result.into()
96    }
97
98    pub fn words(&self) -> Vec<&str> {
100        match self.naming_convention() {
101            NamingConvention::SnakeCase => self.name.split('_').collect(),
102            NamingConvention::KebabCase => self.name.split('-').collect(),
103            NamingConvention::PascalCase => {
104                let indices = self
105                    .name
106                    .chars()
107                    .enumerate()
108                    .filter(|(_, c)| c.is_uppercase())
109                    .map(|(index, _)| index)
110                    .chain(std::iter::once(self.name.len()))
111                    .collect::<Vec<_>>();
112                (0 .. indices.len() - 1)
113                    .map(|i| {
114                        &self.name[indices[i]..=(indices[i + 1] - 1)]
115                    })
116                    .collect()
117            },
118            NamingConvention::CamelCase => {
119                let indices = 
120                    std::iter::once(0)
121                    .chain(self
122                        .name
123                        .chars()
124                        .enumerate()
125                        .filter(|(_, c)| c.is_uppercase())
126                        .map(|(index, _)| index)
127                        .chain(std::iter::once(self.name.len()))
128                    ).collect::<Vec<_>>();
129                (0 .. indices.len() - 1)
130                    .map(|i| {
131                        &self.name[indices[i]..=(indices[i + 1] - 1)]
132                    })
133                    .collect()
134            },
135            NamingConvention::Unknown => vec![self.name.as_str()],
136        }
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use crate::Identifier;
143
144    use super::*;
145
146    #[test]
147    fn kebab_case() {
148        assert_eq!(Identifier::from("kebab-case").naming_convention(), NamingConvention::KebabCase);
149    }
150
151    #[test]
152    fn snake_case() {
153        assert_eq!(Identifier::from("snake_case").naming_convention(), NamingConvention::SnakeCase);
154    }
155
156    #[test]
157    fn pascal_case() {
158        assert_eq!(Identifier::from("PascalCase").naming_convention(), NamingConvention::PascalCase);
159    }
160
161    #[test]
162    fn camel_case() {
163        assert_eq!(Identifier::from("camelCase").naming_convention(), NamingConvention::CamelCase);
164    }
165
166    #[test]
167    fn words() {
168        assert_eq!(Identifier::from("case").words(), vec!["case"]);
169        assert_eq!(Identifier::from("kebab-case").words(), vec!["kebab", "case"]);
170        assert_eq!(Identifier::from("snake_case").words(), vec!["snake", "case"]);
171        assert_eq!(Identifier::from("PascalCase").words(), vec!["Pascal", "Case"]);
172        assert_eq!(Identifier::from("camelCase").words(), vec!["camel", "Case"]);
173    }
174
175    #[test]
176    fn convertion() {
177        assert_eq!(Identifier::from("kebab-case").to_snake_case(), Identifier::from("kebab_case"));
178        assert_eq!(Identifier::from("kebab-case").to_pascal_case(), Identifier::from("KebabCase"));
179        assert_eq!(Identifier::from("kebab-case").to_camel_case(), Identifier::from("kebabCase"));
180        assert_eq!(Identifier::from("snake_case").to_kebab_case(), Identifier::from("snake-case"));
181        assert_eq!(Identifier::from("snake_case").to_pascal_case(), Identifier::from("SnakeCase"));
182        assert_eq!(Identifier::from("snake_case").to_camel_case(), Identifier::from("snakeCase"));
183        assert_eq!(Identifier::from("PascalCase").to_kebab_case(), Identifier::from("pascal-case"));
184        assert_eq!(Identifier::from("PascalCase").to_snake_case(), Identifier::from("pascal_case"));
185        assert_eq!(Identifier::from("PascalCase").to_camel_case(), Identifier::from("pascalCase"));
186        assert_eq!(Identifier::from("camelCase").to_kebab_case(), Identifier::from("camel-case"));
187        assert_eq!(Identifier::from("camelCase").to_snake_case(), Identifier::from("camel_case"));
188        assert_eq!(Identifier::from("camelCase").to_pascal_case(), Identifier::from("CamelCase"));
189    }
190}