pub fn is_keyword_char(c: char, spec: &str) -> bool {
for raw in spec.split(',') {
let token = raw.trim();
if token.is_empty() {
continue;
}
if token == "@" {
if c.is_alphabetic() {
return true;
}
continue;
}
if let Some((lo, hi)) = token.split_once('-')
&& let (Ok(lo), Ok(hi)) = (lo.parse::<u32>(), hi.parse::<u32>())
{
if (lo..=hi).contains(&(c as u32)) {
return true;
}
continue;
}
if let Ok(n) = token.parse::<u32>() {
if c as u32 == n {
return true;
}
continue;
}
let mut chars = token.chars();
if let (Some(only), None) = (chars.next(), chars.next())
&& c == only
{
return true;
}
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn iskeyword_alphabetic_via_at() {
assert!(is_keyword_char('a', "@"));
assert!(is_keyword_char('Z', "@"));
assert!(!is_keyword_char('1', "@"));
}
#[test]
fn iskeyword_numeric_range() {
assert!(is_keyword_char('0', "48-57"));
assert!(is_keyword_char('9', "48-57"));
assert!(!is_keyword_char('a', "48-57"));
}
#[test]
fn iskeyword_literal_punctuation() {
assert!(is_keyword_char('_', "_"));
assert!(!is_keyword_char('.', "_"));
}
#[test]
fn iskeyword_default_spec() {
let spec = "@,48-57,_,192-255";
assert!(is_keyword_char('a', spec));
assert!(is_keyword_char('5', spec));
assert!(is_keyword_char('_', spec));
assert!(!is_keyword_char(' ', spec));
assert!(!is_keyword_char('.', spec));
}
}