pub fn select_plural_form(value: &str, count: i64) -> String {
let forms: Vec<&str> = value.split('|').collect();
match forms.len() {
0 => value.to_string(),
1 => forms[0].trim().to_string(),
2 => {
if count == 1 {
forms[0].trim().to_string()
} else {
forms[1].trim().to_string()
}
}
_ => {
for form in &forms {
let trimmed = form.trim();
if let Some(text) = match_explicit(trimmed, count) {
return text;
}
}
if count == 1 {
strip_prefix_syntax(forms[0].trim()).to_string()
} else {
strip_prefix_syntax(forms.last().unwrap().trim()).to_string()
}
}
}
}
fn match_explicit(form: &str, count: i64) -> Option<String> {
let trimmed = form.trim();
if trimmed.starts_with('{') {
if let Some(close) = trimmed.find('}') {
let num_str = &trimmed[1..close];
if let Ok(n) = num_str.trim().parse::<i64>() {
if n == count {
let text = trimmed[close + 1..].trim();
return Some(text.to_string());
}
}
return None;
}
}
if trimmed.starts_with('[') {
if let Some(close) = trimmed.find(']') {
let range_str = &trimmed[1..close];
let parts: Vec<&str> = range_str.split(',').collect();
if parts.len() == 2 {
let low = parts[0].trim().parse::<i64>().ok();
let high_str = parts[1].trim();
if let Some(low) = low {
let matches = if high_str == "*" {
count >= low
} else if let Ok(high) = high_str.parse::<i64>() {
count >= low && count <= high
} else {
false
};
if matches {
let text = trimmed[close + 1..].trim();
return Some(text.to_string());
}
}
}
return None;
}
}
None
}
fn strip_prefix_syntax(form: &str) -> &str {
let trimmed = form.trim();
if trimmed.starts_with('{') {
if let Some(close) = trimmed.find('}') {
return trimmed[close + 1..].trim();
}
}
if trimmed.starts_with('[') {
if let Some(close) = trimmed.find(']') {
return trimmed[close + 1..].trim();
}
}
trimmed
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn single_form_returns_as_is() {
assert_eq!(select_plural_form("item", 1), "item");
assert_eq!(select_plural_form("item", 5), "item");
}
#[test]
fn two_forms_count_one() {
assert_eq!(select_plural_form("One item|:count items", 1), "One item");
}
#[test]
fn two_forms_count_many() {
assert_eq!(
select_plural_form("One item|:count items", 2),
":count items"
);
}
#[test]
fn two_forms_count_zero() {
assert_eq!(
select_plural_form("One item|:count items", 0),
":count items"
);
}
#[test]
fn explicit_exact_match() {
let value = "{0} No items|{1} One item|[2,*] :count items";
assert_eq!(select_plural_form(value, 0), "No items");
assert_eq!(select_plural_form(value, 1), "One item");
assert_eq!(select_plural_form(value, 5), ":count items");
}
#[test]
fn explicit_range_match() {
let value = "{0} none|[1,5] a few|[6,*] many";
assert_eq!(select_plural_form(value, 0), "none");
assert_eq!(select_plural_form(value, 3), "a few");
assert_eq!(select_plural_form(value, 6), "many");
assert_eq!(select_plural_form(value, 100), "many");
}
#[test]
fn fallback_when_no_range_matches() {
let value = "zero|one|many";
assert_eq!(select_plural_form(value, 1), "zero");
assert_eq!(select_plural_form(value, 5), "many");
}
#[test]
fn whitespace_trimmed() {
assert_eq!(
select_plural_form(" One item | :count items ", 1),
"One item"
);
assert_eq!(
select_plural_form(" One item | :count items ", 2),
":count items"
);
}
}