1pub(crate) fn to_pascal_case(s: &str) -> String {
14 s.split(['-', '_']).map(pascal_segment).collect()
15}
16
17fn pascal_segment(seg: &str) -> String {
19 if seg.is_empty() {
20 return String::new();
21 }
22 let all_caps = seg.chars().all(|c| !c.is_alphabetic() || c.is_uppercase());
23 let mut out = String::with_capacity(seg.len());
24 for (i, c) in seg.chars().enumerate() {
25 if i == 0 {
26 out.push(c.to_ascii_uppercase());
27 } else if all_caps && c.is_alphabetic() {
28 out.push(c.to_ascii_lowercase());
29 } else {
30 out.push(c);
31 }
32 }
33 out
34}
35
36pub(crate) fn to_snake_case(s: &str) -> String {
38 let mut result = String::new();
39 let mut prev_was_upper = false;
40
41 for (i, c) in s.chars().enumerate() {
42 if c == '-' {
43 result.push('_');
44 prev_was_upper = false;
45 } else if c.is_uppercase() {
46 if i > 0 && !prev_was_upper {
47 result.push('_');
48 }
49 result.push(c.to_ascii_lowercase());
50 prev_was_upper = true;
51 } else {
52 result.push(c);
53 prev_was_upper = false;
54 }
55 }
56
57 result
58}
59
60pub(crate) fn to_screaming_snake_case(s: &str) -> String {
62 to_snake_case(s).to_uppercase()
63}
64
65pub fn module_file_stem(name: &str) -> String {
73 let mut out = String::with_capacity(name.len() + 4);
74 let mut prev_was_upper = true;
75 for c in name.chars() {
76 if c.is_uppercase() {
77 if !prev_was_upper && !out.is_empty() {
78 out.push('_');
79 }
80 out.extend(c.to_lowercase());
81 prev_was_upper = true;
82 } else if c.is_alphanumeric() {
83 out.push(c);
84 prev_was_upper = false;
85 } else {
86 if !out.ends_with('_') && !out.is_empty() {
87 out.push('_');
88 }
89 prev_was_upper = true;
90 }
91 }
92 out.trim_end_matches('_').to_string()
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn test_to_pascal_case() {
101 assert_eq!(to_pascal_case("test"), "Test");
102 assert_eq!(to_pascal_case("test-name"), "TestName");
103 assert_eq!(to_pascal_case("test_name"), "TestName");
104 assert_eq!(to_pascal_case("myType"), "MyType");
105 }
106
107 #[test]
108 fn test_to_pascal_case_all_caps_segments() {
109 assert_eq!(to_pascal_case("KDC-REQ"), "KdcReq");
111 assert_eq!(to_pascal_case("KDC-REQ-BODY"), "KdcReqBody");
112 assert_eq!(to_pascal_case("KRB-ERROR"), "KrbError");
113 assert_eq!(to_pascal_case("AP-REQ"), "ApReq");
114 assert_eq!(to_pascal_case("PA-DATA"), "PaData");
115 assert_eq!(to_pascal_case("TGS-REP"), "TgsRep");
116 assert_eq!(to_pascal_case("KRB-SAFE-BODY"), "KrbSafeBody");
117 }
118
119 #[test]
120 fn test_to_pascal_case_mixed_case_preserved() {
121 assert_eq!(to_pascal_case("KerberosString"), "KerberosString");
123 assert_eq!(to_pascal_case("AlgorithmIdentifier"), "AlgorithmIdentifier");
124 assert_eq!(to_pascal_case("EncKDCRepPart"), "EncKDCRepPart");
125 }
126
127 #[test]
128 fn test_to_pascal_case_digits_in_segment() {
129 assert_eq!(to_pascal_case("ETYPE-INFO2"), "EtypeInfo2");
131 assert_eq!(to_pascal_case("ETYPE-INFO2-ENTRY"), "EtypeInfo2Entry");
132 assert_eq!(to_pascal_case("PA-ENC-TS-ENC"), "PaEncTsEnc");
133 }
134
135 #[test]
136 fn test_to_snake_case() {
137 assert_eq!(to_snake_case("Test"), "test");
138 assert_eq!(to_snake_case("TestName"), "test_name");
139 assert_eq!(to_snake_case("test-name"), "test_name");
140 assert_eq!(to_snake_case("camelCase"), "camel_case");
141 }
142
143 #[test]
144 fn test_to_screaming_snake_case() {
145 assert_eq!(to_screaming_snake_case("Test"), "TEST");
146 assert_eq!(to_screaming_snake_case("TestName"), "TEST_NAME");
147 assert_eq!(to_screaming_snake_case("test-name"), "TEST_NAME");
148 }
149
150 #[test]
151 fn test_stem_pascal_case() {
152 assert_eq!(module_file_stem("Certificate"), "certificate");
153 assert_eq!(
154 module_file_stem("AlgorithmIdentifier"),
155 "algorithm_identifier"
156 );
157 }
158
159 #[test]
160 fn test_stem_all_caps() {
161 assert_eq!(module_file_stem("RFC5280"), "rfc5280");
162 }
163
164 #[test]
165 fn test_stem_single_word() {
166 assert_eq!(module_file_stem("PKIX"), "pkix");
167 assert_eq!(module_file_stem("Kerberos"), "kerberos");
168 }
169}