use once_cell::sync::Lazy;
use regex::Regex;
static SET_TOKEN_RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"(?i)set\d+").expect("valid set regex"));
pub fn normalize_set_name(value: impl AsRef<str>) -> Option<String> {
let trimmed = value.as_ref().trim();
if trimmed.is_empty() {
return None;
}
if trimmed.to_ascii_lowercase().starts_with("set") {
return Some(trimmed.to_ascii_lowercase());
}
Some(format!("set{}", trimmed.to_ascii_lowercase()))
}
pub fn parse_set_token(value: impl AsRef<str>) -> Option<String> {
let normalized = normalize_set_name(value)?;
let digits = normalized.strip_prefix("set")?;
if !digits.is_empty() && digits.chars().all(|character| character.is_ascii_digit()) {
Some(normalized)
} else {
None
}
}
pub fn replace_set_tokens(value: impl AsRef<str>, replacement: &str) -> String {
SET_TOKEN_RE
.replace_all(value.as_ref(), replacement)
.into_owned()
}
pub fn find_set_tokens(value: impl AsRef<str>) -> Vec<String> {
SET_TOKEN_RE
.find_iter(value.as_ref())
.filter_map(|item| parse_set_token(item.as_str()))
.collect()
}
#[cfg(test)]
mod tests {
use super::{find_set_tokens, normalize_set_name, parse_set_token, replace_set_tokens};
#[test]
fn normalizes_and_parses_set_names() {
assert_eq!(normalize_set_name("16").as_deref(), Some("set16"));
assert_eq!(normalize_set_name("Set52").as_deref(), Some("set52"));
assert_eq!(parse_set_token("set16").as_deref(), Some("set16"));
assert_eq!(parse_set_token("helmet"), None);
}
#[test]
fn replaces_embedded_set_tokens() {
let replaced = replace_set_tokens(
"models/customization/characters/doomslayer/set52/doomslayer_game_torso_set52.tga",
"set16",
);
assert_eq!(
replaced,
"models/customization/characters/doomslayer/set16/doomslayer_game_torso_set16.tga"
);
assert_eq!(
find_set_tokens("set52/doomslayer_game_torso_set52.tga"),
vec!["set52".to_string(), "set52".to_string()]
);
}
}