gesha_rust_types/
identifier.rs1use heck::ToUpperCamelCase;
2use std::fmt::{Display, Formatter};
3use syn::parse_str;
4use syn::Ident;
5
6#[derive(Clone, Debug, Hash, Eq, PartialEq)]
7pub struct TypeIdentifier(String);
8
9impl TypeIdentifier {
10 pub fn generate<A: AsRef<str>>(a: A) -> Self {
11 let a = a.as_ref();
12 let converted = a.to_upper_camel_case();
13 let result = parse_str::<Ident>(&converted);
14 if result.is_ok() {
15 return Self(converted);
16 }
17 let init: Vec<String> = vec!["".to_string()];
18 let mut converted = a
19 .chars()
20 .fold(init, replace_symbol_with_name)
21 .join("_")
22 .to_upper_camel_case();
23
24 if converted.starts_with(char::is_numeric) {
25 converted = "_".to_string() + &converted;
26 }
27 Self(converted)
29 }
30}
31
32fn replace_symbol_with_name(mut acc: Vec<String>, c: char) -> Vec<String> {
33 match ascii_symbol_to_name(c) {
34 Some(converted) => {
35 acc.push(converted.into());
36 acc.push("".to_string());
37 }
38 _ => {
39 let last = acc.len() - 1;
40 acc[last].push(c);
41 }
42 };
43 acc
44}
45
46impl Display for TypeIdentifier {
47 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48 Display::fmt(&self.0, f)
49 }
50}
51
52impl AsRef<str> for TypeIdentifier {
53 fn as_ref(&self) -> &str {
54 &self.0
55 }
56}
57
58impl From<TypeIdentifier> for String {
59 fn from(this: TypeIdentifier) -> Self {
60 this.0
61 }
62}
63
64impl PartialEq<&str> for TypeIdentifier {
65 fn eq(&self, other: &&str) -> bool {
66 self.0 == *other
67 }
68}
69
70fn ascii_symbol_to_name(c: char) -> Option<&'static str> {
71 let str = match c {
72 ' ' => "space",
73 '!' => "exclamation",
74 '"' => "double_quote",
75 '#' => "hash",
76 '$' => "dollar",
77 '%' => "percent",
78 '&' => "ampersand",
79 '\'' => "apostrophe",
80 '(' => "left_parenthesis",
81 ')' => "right_parenthesis",
82 '*' => "asterisk",
83 '+' => "plus",
84 ',' => "comma",
85 '-' => "minus",
86 '.' => "period",
87 '/' => "slash",
88 ':' => "colon",
89 ';' => "semicolon",
90 '<' => "less_than",
91 '=' => "equals",
92 '>' => "greater_than",
93 '?' => "question",
94 '@' => "at",
95 '[' => "left_bracket",
96 '\\' => "backslash",
97 ']' => "right_bracket",
98 '^' => "caret",
99 '_' => "underscore",
100 '`' => "backtick",
101 '{' => "left_brace",
102 '|' => "pipe",
103 '}' => "right_brace",
104 '~' => "tilde",
105 _ => {
106 return None;
108 }
109 };
110 Some(str)
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn ok_as_it_is() {
119 let actual = TypeIdentifier::generate("hello_world");
120 assert_eq!(actual, "HelloWorld");
121 }
122
123 #[test]
124 fn ok_only_symbol() {
125 let actual = TypeIdentifier::generate("*+-/");
126 let expected = "AsteriskPlusMinusSlash";
127 assert_eq!(actual, expected);
128 }
129
130 #[test]
131 fn ok_starts_with_numeric() {
132 let actual = TypeIdentifier::generate("123foo");
133 let expected = "_123foo";
134 assert_eq!(actual, expected);
135 }
136
137 #[test]
138 fn ok_with_numeric_and_symbol() {
139 let actual = TypeIdentifier::generate("1+foo=345%bar");
140 let expected = "_1PlusFooEquals345PercentBar";
141 assert_eq!(actual, expected);
142 }
143}