codama_nodes/shared/
camel_case_string.rs1use serde::{Deserialize, Serialize};
2use std::ops::Deref;
3
4#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
5pub struct CamelCaseString(String);
6
7impl CamelCaseString {
8 pub fn new<T>(string: T) -> Self
9 where
10 T: AsRef<str>,
11 {
12 Self(to_camel_case(string.as_ref()))
13 }
14}
15
16impl From<CamelCaseString> for String {
17 fn from(val: CamelCaseString) -> Self {
18 val.0
19 }
20}
21
22impl From<String> for CamelCaseString {
23 fn from(string: String) -> Self {
24 Self::new(string)
25 }
26}
27
28impl From<&str> for CamelCaseString {
29 fn from(string: &str) -> Self {
30 Self::new(string)
31 }
32}
33
34impl Deref for CamelCaseString {
35 type Target = String;
36
37 fn deref(&self) -> &Self::Target {
38 &self.0
39 }
40}
41
42impl AsRef<str> for CamelCaseString {
43 fn as_ref(&self) -> &str {
44 &self.0
45 }
46}
47
48fn to_camel_case(input: &str) -> String {
49 let mut result = String::new();
50 let mut new_word = true;
51
52 let chars: Vec<char> = input.chars().collect();
53 let mut i = 0;
54 while i < chars.len() {
55 let c = chars[i];
56
57 if c.is_alphanumeric() {
58 if new_word && !result.is_empty() {
59 result.extend(c.to_uppercase());
61 } else {
62 result.extend(c.to_lowercase());
64 }
65 new_word = false;
66 } else {
67 new_word = true;
68 }
69
70 if c.is_numeric() {
72 new_word = true;
73 }
74
75 if i + 1 < chars.len() && c.is_lowercase() && chars[i + 1].is_uppercase() {
77 new_word = true;
78 }
79
80 i += 1;
81 }
82
83 result
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn parse_from_title_case() {
92 let value = CamelCaseString::new(String::from("Hello This is a Long Title!"));
93 assert_eq!(value.0, "helloThisIsALongTitle");
94 }
95
96 #[test]
97 fn parse_from_numbers() {
98 let value = CamelCaseString::new(String::from("This123 str1ng has 456n numbers"));
99 assert_eq!(value.0, "this123Str1NgHas456NNumbers");
100 }
101
102 #[test]
103 fn parse_from_snake_case() {
104 let value = CamelCaseString::new(String::from("hello_this_is__a_snake_case"));
105 assert_eq!(value.0, "helloThisIsASnakeCase");
106 }
107
108 #[test]
109 fn parse_from_pascal_case() {
110 let value = CamelCaseString::new(String::from("HelloThisIs7PascalCaseWords"));
111 assert_eq!(value.0, "helloThisIs7PascalCaseWords");
112 }
113
114 #[test]
115 fn parse_from_special_chars() {
116 let value = CamelCaseString::new(String::from("crate::hello:world?*,this+is!a#test"));
117 assert_eq!(value.0, "crateHelloWorldThisIsATest");
118 }
119
120 #[test]
121 fn double_parse() {
122 let value = to_camel_case("my_value");
123 let value = to_camel_case(&value);
124 assert_eq!(value, "myValue");
125 }
126
127 #[test]
128 fn new_from_string() {
129 let value = CamelCaseString::new(String::from("my_value"));
130 assert_eq!(value.0, "myValue");
131 }
132
133 #[test]
134 fn new_from_str() {
135 let value = CamelCaseString::new("my_value");
136 assert_eq!(value.0, "myValue");
137 }
138
139 #[test]
140 fn new_from_self() {
141 let value = CamelCaseString::new(CamelCaseString::new("my_value"));
142 assert_eq!(value.0, "myValue");
143 }
144
145 #[test]
146 fn from_string() {
147 let value: CamelCaseString = String::from("my_value").into();
148 assert_eq!(value.0, "myValue");
149 }
150
151 #[test]
152 fn from_str() {
153 let value: CamelCaseString = "my_value".into();
154 assert_eq!(value.0, "myValue");
155 }
156
157 #[test]
158 fn into_string() {
159 let value: String = CamelCaseString::new("my_value").into();
160 assert_eq!(value, "myValue");
161 }
162
163 #[test]
164 fn deref() {
165 let value = CamelCaseString::new("Hello World!");
166 assert_eq!(*value, "helloWorld");
167 }
168
169 #[test]
170 fn as_ref() {
171 let value = CamelCaseString::new("Hello World!");
172 assert_eq!(value.as_ref(), "helloWorld");
173 }
174
175 #[test]
176 fn to_json() {
177 let value = CamelCaseString::new("helloWorld");
178 let json = serde_json::to_string(&value).unwrap();
179 assert_eq!(json, "\"helloWorld\"");
180 }
181
182 #[test]
183 fn from_json() {
184 let json = "\"helloWorld\"";
185 let value: CamelCaseString = serde_json::from_str(json).unwrap();
186 assert_eq!(value, CamelCaseString::new("helloWorld"));
187 }
188}