#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use core::{fmt, str::FromStr};
use std::error::Error;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GoKeywordParseError {
Empty,
Unknown,
}
impl fmt::Display for GoKeywordParseError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => formatter.write_str("Go word cannot be empty"),
Self::Unknown => formatter.write_str("unknown Go keyword or predeclared identifier"),
}
}
}
impl Error for GoKeywordParseError {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum GoKeyword {
Break,
Default,
Func,
Interface,
Select,
Case,
Defer,
Go,
Map,
Struct,
Chan,
Else,
Goto,
Package,
Switch,
Const,
Fallthrough,
If,
Range,
Type,
Continue,
For,
Import,
Return,
Var,
}
impl GoKeyword {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Break => "break",
Self::Default => "default",
Self::Func => "func",
Self::Interface => "interface",
Self::Select => "select",
Self::Case => "case",
Self::Defer => "defer",
Self::Go => "go",
Self::Map => "map",
Self::Struct => "struct",
Self::Chan => "chan",
Self::Else => "else",
Self::Goto => "goto",
Self::Package => "package",
Self::Switch => "switch",
Self::Const => "const",
Self::Fallthrough => "fallthrough",
Self::If => "if",
Self::Range => "range",
Self::Type => "type",
Self::Continue => "continue",
Self::For => "for",
Self::Import => "import",
Self::Return => "return",
Self::Var => "var",
}
}
}
impl fmt::Display for GoKeyword {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
impl FromStr for GoKeyword {
type Err = GoKeywordParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match normalized_word(input)?.as_str() {
"break" => Ok(Self::Break),
"default" => Ok(Self::Default),
"func" => Ok(Self::Func),
"interface" => Ok(Self::Interface),
"select" => Ok(Self::Select),
"case" => Ok(Self::Case),
"defer" => Ok(Self::Defer),
"go" => Ok(Self::Go),
"map" => Ok(Self::Map),
"struct" => Ok(Self::Struct),
"chan" => Ok(Self::Chan),
"else" => Ok(Self::Else),
"goto" => Ok(Self::Goto),
"package" => Ok(Self::Package),
"switch" => Ok(Self::Switch),
"const" => Ok(Self::Const),
"fallthrough" => Ok(Self::Fallthrough),
"if" => Ok(Self::If),
"range" => Ok(Self::Range),
"type" => Ok(Self::Type),
"continue" => Ok(Self::Continue),
"for" => Ok(Self::For),
"import" => Ok(Self::Import),
"return" => Ok(Self::Return),
"var" => Ok(Self::Var),
_ => Err(GoKeywordParseError::Unknown),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum GoPredeclaredIdentifier {
Any,
Bool,
Byte,
Comparable,
Complex64,
Complex128,
Error,
Float32,
Float64,
Int,
Int8,
Int16,
Int32,
Int64,
Rune,
String,
Uint,
Uint8,
Uint16,
Uint32,
Uint64,
Uintptr,
True,
False,
Iota,
Nil,
Append,
Cap,
Clear,
Close,
Complex,
Copy,
Delete,
Imag,
Len,
Make,
Max,
Min,
New,
Panic,
Print,
Println,
Real,
Recover,
}
impl GoPredeclaredIdentifier {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Any => "any",
Self::Bool => "bool",
Self::Byte => "byte",
Self::Comparable => "comparable",
Self::Complex64 => "complex64",
Self::Complex128 => "complex128",
Self::Error => "error",
Self::Float32 => "float32",
Self::Float64 => "float64",
Self::Int => "int",
Self::Int8 => "int8",
Self::Int16 => "int16",
Self::Int32 => "int32",
Self::Int64 => "int64",
Self::Rune => "rune",
Self::String => "string",
Self::Uint => "uint",
Self::Uint8 => "uint8",
Self::Uint16 => "uint16",
Self::Uint32 => "uint32",
Self::Uint64 => "uint64",
Self::Uintptr => "uintptr",
Self::True => "true",
Self::False => "false",
Self::Iota => "iota",
Self::Nil => "nil",
Self::Append => "append",
Self::Cap => "cap",
Self::Clear => "clear",
Self::Close => "close",
Self::Complex => "complex",
Self::Copy => "copy",
Self::Delete => "delete",
Self::Imag => "imag",
Self::Len => "len",
Self::Make => "make",
Self::Max => "max",
Self::Min => "min",
Self::New => "new",
Self::Panic => "panic",
Self::Print => "print",
Self::Println => "println",
Self::Real => "real",
Self::Recover => "recover",
}
}
}
impl fmt::Display for GoPredeclaredIdentifier {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
impl FromStr for GoPredeclaredIdentifier {
type Err = GoKeywordParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match normalized_word(input)?.as_str() {
"any" => Ok(Self::Any),
"bool" => Ok(Self::Bool),
"byte" => Ok(Self::Byte),
"comparable" => Ok(Self::Comparable),
"complex64" => Ok(Self::Complex64),
"complex128" => Ok(Self::Complex128),
"error" => Ok(Self::Error),
"float32" => Ok(Self::Float32),
"float64" => Ok(Self::Float64),
"int" => Ok(Self::Int),
"int8" => Ok(Self::Int8),
"int16" => Ok(Self::Int16),
"int32" => Ok(Self::Int32),
"int64" => Ok(Self::Int64),
"rune" => Ok(Self::Rune),
"string" => Ok(Self::String),
"uint" => Ok(Self::Uint),
"uint8" => Ok(Self::Uint8),
"uint16" => Ok(Self::Uint16),
"uint32" => Ok(Self::Uint32),
"uint64" => Ok(Self::Uint64),
"uintptr" => Ok(Self::Uintptr),
"true" => Ok(Self::True),
"false" => Ok(Self::False),
"iota" => Ok(Self::Iota),
"nil" => Ok(Self::Nil),
"append" => Ok(Self::Append),
"cap" => Ok(Self::Cap),
"clear" => Ok(Self::Clear),
"close" => Ok(Self::Close),
"complex" => Ok(Self::Complex),
"copy" => Ok(Self::Copy),
"delete" => Ok(Self::Delete),
"imag" => Ok(Self::Imag),
"len" => Ok(Self::Len),
"make" => Ok(Self::Make),
"max" => Ok(Self::Max),
"min" => Ok(Self::Min),
"new" => Ok(Self::New),
"panic" => Ok(Self::Panic),
"print" => Ok(Self::Print),
"println" => Ok(Self::Println),
"real" => Ok(Self::Real),
"recover" => Ok(Self::Recover),
_ => Err(GoKeywordParseError::Unknown),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum GoReservedWord {
Keyword(GoKeyword),
PredeclaredIdentifier(GoPredeclaredIdentifier),
}
impl GoReservedWord {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Keyword(keyword) => keyword.as_str(),
Self::PredeclaredIdentifier(identifier) => identifier.as_str(),
}
}
}
impl fmt::Display for GoReservedWord {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
impl FromStr for GoReservedWord {
type Err = GoKeywordParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
if let Ok(keyword) = input.parse::<GoKeyword>() {
return Ok(Self::Keyword(keyword));
}
input
.parse::<GoPredeclaredIdentifier>()
.map(Self::PredeclaredIdentifier)
}
}
#[must_use]
pub fn is_go_keyword(input: &str) -> bool {
input.parse::<GoKeyword>().is_ok()
}
#[must_use]
pub fn is_go_predeclared_identifier(input: &str) -> bool {
input.parse::<GoPredeclaredIdentifier>().is_ok()
}
#[must_use]
pub fn is_go_reserved_word(input: &str) -> bool {
input.parse::<GoReservedWord>().is_ok()
}
fn normalized_word(input: &str) -> Result<String, GoKeywordParseError> {
let trimmed = input.trim();
if trimmed.is_empty() {
Err(GoKeywordParseError::Empty)
} else {
Ok(trimmed.to_ascii_lowercase())
}
}
#[cfg(test)]
mod tests {
use super::{
is_go_keyword, is_go_predeclared_identifier, is_go_reserved_word, GoKeyword,
GoKeywordParseError, GoPredeclaredIdentifier, GoReservedWord,
};
#[test]
fn parses_keywords() -> Result<(), GoKeywordParseError> {
let keyword: GoKeyword = "Func".parse()?;
assert_eq!(keyword, GoKeyword::Func);
assert_eq!(keyword.to_string(), "func");
assert!(is_go_keyword("package"));
assert!(!is_go_keyword("nil"));
Ok(())
}
#[test]
fn parses_predeclared_identifiers() -> Result<(), GoKeywordParseError> {
let identifier: GoPredeclaredIdentifier = "complex128".parse()?;
assert_eq!(identifier, GoPredeclaredIdentifier::Complex128);
assert_eq!(GoPredeclaredIdentifier::Uintptr.to_string(), "uintptr");
assert!(is_go_predeclared_identifier("nil"));
assert!(is_go_predeclared_identifier("println"));
Ok(())
}
#[test]
fn checks_reserved_words() -> Result<(), GoKeywordParseError> {
let reserved: GoReservedWord = "interface".parse()?;
assert_eq!(reserved.as_str(), "interface");
assert!(is_go_reserved_word("true"));
assert!(is_go_reserved_word("return"));
assert!(!is_go_reserved_word("handler"));
Ok(())
}
#[test]
fn rejects_empty_and_unknown_words() {
assert_eq!("".parse::<GoKeyword>(), Err(GoKeywordParseError::Empty));
assert_eq!(
"handler".parse::<GoKeyword>(),
Err(GoKeywordParseError::Unknown)
);
}
}