myrmic_common/cells/
names.rs1use alloc::{borrow::ToOwned, string::String};
2use serde::{Deserialize, Serialize};
3
4#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
6pub struct Command(String);
7
8#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
10pub struct Event(String);
11
12macro_rules! impl_name_type {
14 ($ty: ident) => {
15 impl $ty {
16 #[doc = concat!("Validates `name` and wraps it as a `", stringify!($ty), "`.")]
17 pub fn new(name: String) -> Result<Self, &'static str> {
18 validate_function_name_component(&name)?;
19 Ok(Self(name))
20 }
21 }
22
23 impl AsRef<str> for $ty {
24 fn as_ref(&self) -> &str {
25 &self.0
26 }
27 }
28
29 impl TryFrom<&str> for $ty {
30 type Error = &'static str;
31
32 fn try_from(value: &str) -> Result<Self, Self::Error> {
33 $ty::new(value.to_owned())
34 }
35 }
36
37 impl TryFrom<String> for $ty {
38 type Error = &'static str;
39
40 fn try_from(value: String) -> Result<Self, Self::Error> {
41 $ty::new(value)
42 }
43 }
44
45 impl alloc::str::FromStr for $ty {
47 type Err = &'static str;
48
49 fn from_str(s: &str) -> Result<Self, Self::Err> {
50 $ty::new(s.to_owned())
51 }
52 }
53 };
54}
55
56impl_name_type!(Command);
58impl_name_type!(Event);
59
60fn validate_function_name_component(input: &str) -> Result<(), &'static str> {
66 if input.is_empty() {
68 return Err("name cannot be empty");
69 }
70
71 if input.contains(char::is_whitespace) {
72 return Err("name cannot contain whitespace");
73 }
74
75 if !input.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
77 return Err("name can only contain ASCII alphanumeric characters and underscores");
78 }
79
80 Ok(())
81}
82
83#[cfg(test)]
84mod tests {
85
86 use super::validate_function_name_component;
89 use claims::{assert_err, assert_ok};
90
91 #[test]
92 fn validate_function_name_component_valid_cases() {
93 assert_ok!(validate_function_name_component("my_event"));
95 assert_ok!(validate_function_name_component("MyEvent"));
96 assert_ok!(validate_function_name_component("event123"));
97 assert_ok!(validate_function_name_component("_event"));
98 assert_ok!(validate_function_name_component("event_name_123"));
99
100 assert_ok!(validate_function_name_component("123event"));
102 assert_ok!(validate_function_name_component("0"));
103
104 assert_ok!(validate_function_name_component("_"));
106 assert_ok!(validate_function_name_component("__"));
107 assert_ok!(validate_function_name_component(
108 "event_name_with_underscores"
109 ));
110 }
111
112 #[test]
113 fn validate_function_name_component_empty_string() {
114 assert_err!(validate_function_name_component(""));
115 }
116
117 #[test]
118 fn validate_function_name_component_whitespace_leading() {
119 assert_err!(validate_function_name_component(" event"));
120 assert_err!(validate_function_name_component(" event"));
121 assert_err!(validate_function_name_component("\tevent"));
122 assert_err!(validate_function_name_component("\nevent"));
123 assert_err!(validate_function_name_component("\revent"));
124 }
125
126 #[test]
127 fn validate_function_name_component_whitespace_trailing() {
128 assert_err!(validate_function_name_component("event "));
129 assert_err!(validate_function_name_component("event "));
130 assert_err!(validate_function_name_component("event\t"));
131 assert_err!(validate_function_name_component("event\n"));
132 assert_err!(validate_function_name_component("event\r"));
133 }
134
135 #[test]
136 fn validate_function_name_component_whitespace_middle() {
137 assert_err!(validate_function_name_component("my event"));
138 assert_err!(validate_function_name_component("my event"));
139 assert_err!(validate_function_name_component("my\tevent"));
140 assert_err!(validate_function_name_component("my\nevent"));
141 assert_err!(validate_function_name_component("my\revent"));
142 assert_err!(validate_function_name_component("my event name"));
143 }
144
145 #[test]
146 fn validate_function_name_component_special_characters() {
147 assert_err!(validate_function_name_component("event!"));
149 assert_err!(validate_function_name_component("event@"));
150 assert_err!(validate_function_name_component("event#"));
151 assert_err!(validate_function_name_component("event$"));
152 assert_err!(validate_function_name_component("event%"));
153 assert_err!(validate_function_name_component("event^"));
154 assert_err!(validate_function_name_component("event&"));
155 assert_err!(validate_function_name_component("event*"));
156 assert_err!(validate_function_name_component("event("));
157 assert_err!(validate_function_name_component("event)"));
158 assert_err!(validate_function_name_component("event-"));
159 assert_err!(validate_function_name_component("event+"));
160 assert_err!(validate_function_name_component("event="));
161 assert_err!(validate_function_name_component("event["));
162 assert_err!(validate_function_name_component("event]"));
163 assert_err!(validate_function_name_component("event{"));
164 assert_err!(validate_function_name_component("event}"));
165 assert_err!(validate_function_name_component("event|"));
166 assert_err!(validate_function_name_component("event\\"));
167 assert_err!(validate_function_name_component("event:"));
168 assert_err!(validate_function_name_component("event;"));
169 assert_err!(validate_function_name_component("event\""));
170 assert_err!(validate_function_name_component("event'"));
171 assert_err!(validate_function_name_component("event<"));
172 assert_err!(validate_function_name_component("event>"));
173 assert_err!(validate_function_name_component("event,"));
174 assert_err!(validate_function_name_component("event."));
175 assert_err!(validate_function_name_component("event?"));
176 assert_err!(validate_function_name_component("event/"));
177 assert_err!(validate_function_name_component("event~"));
178 assert_err!(validate_function_name_component("event`"));
179 }
180
181 #[test]
182 fn validate_function_name_component_unicode_characters() {
183 assert_err!(validate_function_name_component("évént"));
185 assert_err!(validate_function_name_component("event中文"));
186 assert_err!(validate_function_name_component("event🚀"));
187 assert_err!(validate_function_name_component("eventα"));
188 assert_err!(validate_function_name_component("event→"));
189 }
190
191 #[test]
192 fn validate_function_name_component_mixed_invalid() {
193 assert_err!(validate_function_name_component(" my event! "));
195 assert_err!(validate_function_name_component("event@123"));
196 assert_err!(validate_function_name_component("event-name"));
197 assert_err!(validate_function_name_component("event.name"));
198 }
199
200 #[test]
201 fn validate_function_name_component_edge_cases() {
202 assert_err!(validate_function_name_component(" "));
204 assert_err!(validate_function_name_component(" "));
205 assert_err!(validate_function_name_component("\t"));
206 assert_err!(validate_function_name_component("\n"));
207
208 assert_err!(validate_function_name_component("!@#$"));
210
211 assert_ok!(validate_function_name_component("a")); assert_ok!(validate_function_name_component("A")); assert_ok!(validate_function_name_component("1")); assert_ok!(validate_function_name_component("_")); }
217}