use phf::phf_map;
pub trait CheckKeyword {
fn is_keyword(&self) -> bool;
fn keyword_status(&self) -> KeywordStatus;
fn into_safe(self) -> String;
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum KeywordStatus {
NotKeyword,
Strict {
can_be_raw: bool,
},
Reserved,
Weak {
restriction: WeakRestriction,
},
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum WeakRestriction {
None,
LifetimeOrLoop,
Dyn,
}
use KeywordStatus::*;
use WeakRestriction::*;
static KEYWORDS: phf::Map<&'static str, KeywordStatus> = phf_map! {
"as" => Strict { can_be_raw: true },
"break" => Strict { can_be_raw: true },
"const" => Strict { can_be_raw: true },
"continue" => Strict { can_be_raw: true },
"crate" => Strict { can_be_raw: false },
"else" => Strict { can_be_raw: true },
"enum" => Strict { can_be_raw: true },
"extern" => Strict { can_be_raw: true },
"false" => Strict { can_be_raw: true },
"fn" => Strict { can_be_raw: true },
"for" => Strict { can_be_raw: true },
"if" => Strict { can_be_raw: true },
"impl" => Strict { can_be_raw: true },
"in" => Strict { can_be_raw: true },
"let" => Strict { can_be_raw: true },
"loop" => Strict { can_be_raw: true },
"match" => Strict { can_be_raw: true },
"mod" => Strict { can_be_raw: true },
"move" => Strict { can_be_raw: true },
"mut" => Strict { can_be_raw: true },
"pub" => Strict { can_be_raw: true },
"ref" => Strict { can_be_raw: true },
"return" => Strict { can_be_raw: true },
"self" => Strict { can_be_raw: false },
"Self" => Strict { can_be_raw: false },
"static" => Strict { can_be_raw: true },
"struct" => Strict { can_be_raw: true },
"super" => Strict { can_be_raw: false },
"trait" => Strict { can_be_raw: true },
"true" => Strict { can_be_raw: true },
"type" => Strict { can_be_raw: true },
"unsafe" => Strict { can_be_raw: true },
"use" => Strict { can_be_raw: true },
"where" => Strict { can_be_raw: true },
"while" => Strict { can_be_raw: true },
"async" => if cfg!(feature = "2018") { Strict { can_be_raw: true } } else { NotKeyword },
"await" => if cfg!(feature = "2018") { Strict { can_be_raw: true } } else { NotKeyword },
"dyn" => if cfg!(feature = "2018") {
Strict { can_be_raw: true }
} else {
Weak { restriction: Dyn }
},
"abstract" => Reserved,
"become" => Reserved,
"box" => Reserved,
"do" => Reserved,
"final" => Reserved,
"macro" => Reserved,
"override" => Reserved,
"priv" => Reserved,
"typeof" => Reserved,
"unsized" => Reserved,
"virtual" => Reserved,
"yield" => Reserved,
"try" => if cfg!(feature = "2018") { Reserved } else { NotKeyword },
"gen" => if cfg!(feature = "2024") { Reserved } else { NotKeyword },
"macro_rules" => Weak { restriction: None },
"union" => Weak { restriction: None },
"'static" => Weak { restriction: LifetimeOrLoop }
};
impl<T: AsRef<str>> CheckKeyword for T {
fn is_keyword(&self) -> bool {
matches!(self.keyword_status(), Strict { .. } | Reserved)
}
fn keyword_status(&self) -> KeywordStatus {
*KEYWORDS.get(self.as_ref()).unwrap_or(&NotKeyword)
}
fn into_safe(self) -> String {
let self_ref = self.as_ref();
match self.keyword_status() {
Strict { can_be_raw: false }
| Weak {
restriction: LifetimeOrLoop,
} => format!("{self_ref}_"),
Strict { .. } | Reserved | Weak { restriction: Dyn } => format!("r#{self_ref}"),
_ => self_ref.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_keyword() {
assert!(String::from("match").is_keyword());
assert!(!"hello".is_keyword());
assert!("crate".is_keyword());
assert_eq!(String::from("async").is_keyword(), cfg!(feature = "2018"));
assert_eq!(String::from("gen").is_keyword(), cfg!(feature = "2024"));
}
#[test]
fn keyword_status() {
assert_eq!("asdf".keyword_status(), NotKeyword);
assert_eq!(
"dyn".keyword_status(),
if cfg!(feature = "2018") {
Strict { can_be_raw: true }
} else {
Weak { restriction: Dyn }
}
);
assert_eq!(
"'static".keyword_status(),
Weak {
restriction: LifetimeOrLoop
}
);
}
#[test]
fn into_safe() {
assert_eq!(String::from("match").into_safe(), "r#match");
assert_eq!("asdf".into_safe(), "asdf");
assert_eq!(
"await".into_safe(),
if cfg!(feature = "2018") {
"r#await"
} else {
"await"
}
);
assert_eq!("self".into_safe(), "self_");
}
}