1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub enum GoKeywordParseError {
10 Empty,
11 Unknown,
12}
13
14impl fmt::Display for GoKeywordParseError {
15 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
16 match self {
17 Self::Empty => formatter.write_str("Go word cannot be empty"),
18 Self::Unknown => formatter.write_str("unknown Go keyword or predeclared identifier"),
19 }
20 }
21}
22
23impl Error for GoKeywordParseError {}
24
25#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
27pub enum GoKeyword {
28 Break,
29 Default,
30 Func,
31 Interface,
32 Select,
33 Case,
34 Defer,
35 Go,
36 Map,
37 Struct,
38 Chan,
39 Else,
40 Goto,
41 Package,
42 Switch,
43 Const,
44 Fallthrough,
45 If,
46 Range,
47 Type,
48 Continue,
49 For,
50 Import,
51 Return,
52 Var,
53}
54
55impl GoKeyword {
56 #[must_use]
58 pub const fn as_str(self) -> &'static str {
59 match self {
60 Self::Break => "break",
61 Self::Default => "default",
62 Self::Func => "func",
63 Self::Interface => "interface",
64 Self::Select => "select",
65 Self::Case => "case",
66 Self::Defer => "defer",
67 Self::Go => "go",
68 Self::Map => "map",
69 Self::Struct => "struct",
70 Self::Chan => "chan",
71 Self::Else => "else",
72 Self::Goto => "goto",
73 Self::Package => "package",
74 Self::Switch => "switch",
75 Self::Const => "const",
76 Self::Fallthrough => "fallthrough",
77 Self::If => "if",
78 Self::Range => "range",
79 Self::Type => "type",
80 Self::Continue => "continue",
81 Self::For => "for",
82 Self::Import => "import",
83 Self::Return => "return",
84 Self::Var => "var",
85 }
86 }
87}
88
89impl fmt::Display for GoKeyword {
90 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
91 formatter.write_str(self.as_str())
92 }
93}
94
95impl FromStr for GoKeyword {
96 type Err = GoKeywordParseError;
97
98 fn from_str(input: &str) -> Result<Self, Self::Err> {
99 match normalized_word(input)?.as_str() {
100 "break" => Ok(Self::Break),
101 "default" => Ok(Self::Default),
102 "func" => Ok(Self::Func),
103 "interface" => Ok(Self::Interface),
104 "select" => Ok(Self::Select),
105 "case" => Ok(Self::Case),
106 "defer" => Ok(Self::Defer),
107 "go" => Ok(Self::Go),
108 "map" => Ok(Self::Map),
109 "struct" => Ok(Self::Struct),
110 "chan" => Ok(Self::Chan),
111 "else" => Ok(Self::Else),
112 "goto" => Ok(Self::Goto),
113 "package" => Ok(Self::Package),
114 "switch" => Ok(Self::Switch),
115 "const" => Ok(Self::Const),
116 "fallthrough" => Ok(Self::Fallthrough),
117 "if" => Ok(Self::If),
118 "range" => Ok(Self::Range),
119 "type" => Ok(Self::Type),
120 "continue" => Ok(Self::Continue),
121 "for" => Ok(Self::For),
122 "import" => Ok(Self::Import),
123 "return" => Ok(Self::Return),
124 "var" => Ok(Self::Var),
125 _ => Err(GoKeywordParseError::Unknown),
126 }
127 }
128}
129
130#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
132pub enum GoPredeclaredIdentifier {
133 Any,
134 Bool,
135 Byte,
136 Comparable,
137 Complex64,
138 Complex128,
139 Error,
140 Float32,
141 Float64,
142 Int,
143 Int8,
144 Int16,
145 Int32,
146 Int64,
147 Rune,
148 String,
149 Uint,
150 Uint8,
151 Uint16,
152 Uint32,
153 Uint64,
154 Uintptr,
155 True,
156 False,
157 Iota,
158 Nil,
159 Append,
160 Cap,
161 Clear,
162 Close,
163 Complex,
164 Copy,
165 Delete,
166 Imag,
167 Len,
168 Make,
169 Max,
170 Min,
171 New,
172 Panic,
173 Print,
174 Println,
175 Real,
176 Recover,
177}
178
179impl GoPredeclaredIdentifier {
180 #[must_use]
182 pub const fn as_str(self) -> &'static str {
183 match self {
184 Self::Any => "any",
185 Self::Bool => "bool",
186 Self::Byte => "byte",
187 Self::Comparable => "comparable",
188 Self::Complex64 => "complex64",
189 Self::Complex128 => "complex128",
190 Self::Error => "error",
191 Self::Float32 => "float32",
192 Self::Float64 => "float64",
193 Self::Int => "int",
194 Self::Int8 => "int8",
195 Self::Int16 => "int16",
196 Self::Int32 => "int32",
197 Self::Int64 => "int64",
198 Self::Rune => "rune",
199 Self::String => "string",
200 Self::Uint => "uint",
201 Self::Uint8 => "uint8",
202 Self::Uint16 => "uint16",
203 Self::Uint32 => "uint32",
204 Self::Uint64 => "uint64",
205 Self::Uintptr => "uintptr",
206 Self::True => "true",
207 Self::False => "false",
208 Self::Iota => "iota",
209 Self::Nil => "nil",
210 Self::Append => "append",
211 Self::Cap => "cap",
212 Self::Clear => "clear",
213 Self::Close => "close",
214 Self::Complex => "complex",
215 Self::Copy => "copy",
216 Self::Delete => "delete",
217 Self::Imag => "imag",
218 Self::Len => "len",
219 Self::Make => "make",
220 Self::Max => "max",
221 Self::Min => "min",
222 Self::New => "new",
223 Self::Panic => "panic",
224 Self::Print => "print",
225 Self::Println => "println",
226 Self::Real => "real",
227 Self::Recover => "recover",
228 }
229 }
230}
231
232impl fmt::Display for GoPredeclaredIdentifier {
233 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
234 formatter.write_str(self.as_str())
235 }
236}
237
238impl FromStr for GoPredeclaredIdentifier {
239 type Err = GoKeywordParseError;
240
241 fn from_str(input: &str) -> Result<Self, Self::Err> {
242 match normalized_word(input)?.as_str() {
243 "any" => Ok(Self::Any),
244 "bool" => Ok(Self::Bool),
245 "byte" => Ok(Self::Byte),
246 "comparable" => Ok(Self::Comparable),
247 "complex64" => Ok(Self::Complex64),
248 "complex128" => Ok(Self::Complex128),
249 "error" => Ok(Self::Error),
250 "float32" => Ok(Self::Float32),
251 "float64" => Ok(Self::Float64),
252 "int" => Ok(Self::Int),
253 "int8" => Ok(Self::Int8),
254 "int16" => Ok(Self::Int16),
255 "int32" => Ok(Self::Int32),
256 "int64" => Ok(Self::Int64),
257 "rune" => Ok(Self::Rune),
258 "string" => Ok(Self::String),
259 "uint" => Ok(Self::Uint),
260 "uint8" => Ok(Self::Uint8),
261 "uint16" => Ok(Self::Uint16),
262 "uint32" => Ok(Self::Uint32),
263 "uint64" => Ok(Self::Uint64),
264 "uintptr" => Ok(Self::Uintptr),
265 "true" => Ok(Self::True),
266 "false" => Ok(Self::False),
267 "iota" => Ok(Self::Iota),
268 "nil" => Ok(Self::Nil),
269 "append" => Ok(Self::Append),
270 "cap" => Ok(Self::Cap),
271 "clear" => Ok(Self::Clear),
272 "close" => Ok(Self::Close),
273 "complex" => Ok(Self::Complex),
274 "copy" => Ok(Self::Copy),
275 "delete" => Ok(Self::Delete),
276 "imag" => Ok(Self::Imag),
277 "len" => Ok(Self::Len),
278 "make" => Ok(Self::Make),
279 "max" => Ok(Self::Max),
280 "min" => Ok(Self::Min),
281 "new" => Ok(Self::New),
282 "panic" => Ok(Self::Panic),
283 "print" => Ok(Self::Print),
284 "println" => Ok(Self::Println),
285 "real" => Ok(Self::Real),
286 "recover" => Ok(Self::Recover),
287 _ => Err(GoKeywordParseError::Unknown),
288 }
289 }
290}
291
292#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
294pub enum GoReservedWord {
295 Keyword(GoKeyword),
296 PredeclaredIdentifier(GoPredeclaredIdentifier),
297}
298
299impl GoReservedWord {
300 #[must_use]
302 pub const fn as_str(self) -> &'static str {
303 match self {
304 Self::Keyword(keyword) => keyword.as_str(),
305 Self::PredeclaredIdentifier(identifier) => identifier.as_str(),
306 }
307 }
308}
309
310impl fmt::Display for GoReservedWord {
311 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
312 formatter.write_str(self.as_str())
313 }
314}
315
316impl FromStr for GoReservedWord {
317 type Err = GoKeywordParseError;
318
319 fn from_str(input: &str) -> Result<Self, Self::Err> {
320 if let Ok(keyword) = input.parse::<GoKeyword>() {
321 return Ok(Self::Keyword(keyword));
322 }
323 input
324 .parse::<GoPredeclaredIdentifier>()
325 .map(Self::PredeclaredIdentifier)
326 }
327}
328
329#[must_use]
331pub fn is_go_keyword(input: &str) -> bool {
332 input.parse::<GoKeyword>().is_ok()
333}
334
335#[must_use]
337pub fn is_go_predeclared_identifier(input: &str) -> bool {
338 input.parse::<GoPredeclaredIdentifier>().is_ok()
339}
340
341#[must_use]
343pub fn is_go_reserved_word(input: &str) -> bool {
344 input.parse::<GoReservedWord>().is_ok()
345}
346
347fn normalized_word(input: &str) -> Result<String, GoKeywordParseError> {
348 let trimmed = input.trim();
349 if trimmed.is_empty() {
350 Err(GoKeywordParseError::Empty)
351 } else {
352 Ok(trimmed.to_ascii_lowercase())
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use super::{
359 is_go_keyword, is_go_predeclared_identifier, is_go_reserved_word, GoKeyword,
360 GoKeywordParseError, GoPredeclaredIdentifier, GoReservedWord,
361 };
362
363 #[test]
364 fn parses_keywords() -> Result<(), GoKeywordParseError> {
365 let keyword: GoKeyword = "Func".parse()?;
366 assert_eq!(keyword, GoKeyword::Func);
367 assert_eq!(keyword.to_string(), "func");
368 assert!(is_go_keyword("package"));
369 assert!(!is_go_keyword("nil"));
370 Ok(())
371 }
372
373 #[test]
374 fn parses_predeclared_identifiers() -> Result<(), GoKeywordParseError> {
375 let identifier: GoPredeclaredIdentifier = "complex128".parse()?;
376 assert_eq!(identifier, GoPredeclaredIdentifier::Complex128);
377 assert_eq!(GoPredeclaredIdentifier::Uintptr.to_string(), "uintptr");
378 assert!(is_go_predeclared_identifier("nil"));
379 assert!(is_go_predeclared_identifier("println"));
380 Ok(())
381 }
382
383 #[test]
384 fn checks_reserved_words() -> Result<(), GoKeywordParseError> {
385 let reserved: GoReservedWord = "interface".parse()?;
386 assert_eq!(reserved.as_str(), "interface");
387 assert!(is_go_reserved_word("true"));
388 assert!(is_go_reserved_word("return"));
389 assert!(!is_go_reserved_word("handler"));
390 Ok(())
391 }
392
393 #[test]
394 fn rejects_empty_and_unknown_words() {
395 assert_eq!("".parse::<GoKeyword>(), Err(GoKeywordParseError::Empty));
396 assert_eq!(
397 "handler".parse::<GoKeyword>(),
398 Err(GoKeywordParseError::Unknown)
399 );
400 }
401}