use std::borrow::Cow;
pub fn is_valid_tag_name(name: &str) -> bool {
let mut chars = name.chars();
match chars.next() {
Some(first) if first.is_ascii_lowercase() => {
chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
}
_ => false,
}
}
pub fn to_tag_name(name: &str) -> Cow<'_, str> {
if is_valid_tag_name(name) {
return Cow::Borrowed(name);
}
let char_count = name.chars().count();
let last_idx = char_count.saturating_sub(1);
let mut step1 = String::with_capacity(name.len());
for (i, c) in name.chars().enumerate() {
match c {
'.' | '-' | '/' => {
if i == 0 {
step1.push('v');
} else if i != last_idx {
step1.push('_');
}
}
_ => step1.push(c),
}
}
let step2 = if step1.starts_with(|c: char| c.is_ascii_digit() || c == '_') {
let mut s = String::with_capacity(step1.len() + 1);
s.push('v');
s.push_str(&step1);
s
} else {
step1
};
let step3 = step2
.chars()
.filter(|&c| c.is_ascii_alphanumeric() || c == '_' || c == ' ')
.collect::<String>();
let step3 = step3.trim();
let result: String = step3
.split(' ')
.filter(|part| !part.is_empty())
.enumerate()
.map(|(i, part)| {
let mut chars = part.chars();
let start = chars.next().expect("part is non-empty after filter");
if i == 0 {
if start.is_ascii_uppercase() {
let mut new_part = String::with_capacity(part.len());
let mut caps_prefix = true;
for ch in part.chars() {
if caps_prefix && ch.is_ascii_uppercase() {
new_part.push(ch.to_ascii_lowercase());
} else {
caps_prefix = false;
new_part.push(ch);
}
}
new_part
} else {
part.to_string()
}
} else {
if start.is_ascii_alphabetic() && start.is_ascii_lowercase() {
let mut new_part = String::with_capacity(part.len());
new_part.push(start.to_ascii_uppercase());
new_part.push_str(&part[1..]);
new_part
} else {
part.to_string()
}
}
})
.collect();
if result.is_empty() {
Cow::Borrowed("empty")
} else {
Cow::Owned(result)
}
}