use std::collections::BTreeSet;
pub(super) fn alphabetical_name(index: usize) -> Option<String> {
const NAMES: &[&str] = &[
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "m", "n", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z",
];
NAMES.get(index).map(|name| (*name).to_owned())
}
pub(super) fn as_valid_name(value: &Option<String>) -> Option<String> {
value.as_deref().and_then(normalize_identifier)
}
pub(super) fn normalize_identifier(candidate: &str) -> Option<String> {
if candidate.is_empty() {
return None;
}
if is_valid_identifier(candidate) {
return Some(candidate.to_owned());
}
let mut normalized = String::with_capacity(candidate.len());
for ch in candidate.chars() {
if ch.is_ascii_alphanumeric() || ch == '_' {
normalized.push(ch);
} else if !normalized.ends_with('_') {
normalized.push('_');
}
}
let normalized = normalized.trim_matches('_');
if normalized.is_empty() {
return None;
}
let mut result = normalized.to_owned();
if result
.chars()
.next()
.is_some_and(|first| first.is_ascii_digit())
{
result.insert(0, '_');
}
if is_valid_identifier(&result) {
Some(result)
} else {
None
}
}
pub(super) fn is_valid_identifier(candidate: &str) -> bool {
let mut chars = candidate.chars();
let Some(first) = chars.next() else {
return false;
};
if !(first.is_ascii_alphabetic() || first == '_') {
return false;
}
chars.all(|ch| ch.is_ascii_alphanumeric() || ch == '_')
}
const LUA_KEYWORDS: &[&str] = &[
"and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in",
"local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", "global",
];
pub(super) fn is_lua_keyword(candidate: &str) -> bool {
LUA_KEYWORDS.contains(&candidate)
}
pub(super) fn lua_keywords() -> BTreeSet<String> {
LUA_KEYWORDS.iter().map(|s| (*s).to_owned()).collect()
}