Skip to main content

use_go_keyword/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7/// Error returned when parsing Go vocabulary fails.
8#[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/// Go source keywords.
26#[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    /// Returns the keyword label as it appears in Go source.
57    #[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/// Common Go predeclared identifiers.
131#[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    /// Returns the identifier label as it appears in Go source.
181    #[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/// Go reserved vocabulary: keywords plus common predeclared identifiers.
293#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
294pub enum GoReservedWord {
295    Keyword(GoKeyword),
296    PredeclaredIdentifier(GoPredeclaredIdentifier),
297}
298
299impl GoReservedWord {
300    /// Returns the reserved word label as it appears in Go source.
301    #[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/// Returns whether `input` is a Go keyword.
330#[must_use]
331pub fn is_go_keyword(input: &str) -> bool {
332    input.parse::<GoKeyword>().is_ok()
333}
334
335/// Returns whether `input` is a common Go predeclared identifier.
336#[must_use]
337pub fn is_go_predeclared_identifier(input: &str) -> bool {
338    input.parse::<GoPredeclaredIdentifier>().is_ok()
339}
340
341/// Returns whether `input` is a Go keyword or common predeclared identifier.
342#[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}