use alloc::string::{String, ToString};
pub use crate::cldr::{ListPatterns, ListSpec};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ListStyle {
And,
Or,
}
fn fmt2(pat: &str, a: &str, b: &str) -> String {
let mut out = String::with_capacity(pat.len() + a.len() + b.len());
push_fmt2(&mut out, pat, a, b);
out
}
fn push_fmt2(out: &mut String, pat: &str, a: &str, b: &str) {
let mut rest = pat;
while !rest.is_empty() {
if let Some(r) = rest.strip_prefix("{0}") {
out.push_str(a);
rest = r;
} else if let Some(r) = rest.strip_prefix("{1}") {
out.push_str(b);
rest = r;
} else {
let ch = rest.chars().next().unwrap();
out.push(ch);
rest = &rest[ch.len_utf8()..];
}
}
}
fn split2(pat: &str) -> (&str, &str, &str) {
let (pre, after0) = match pat.find("{0}") {
Some(i) => (&pat[..i], &pat[i + 3..]),
None => ("", pat),
};
let (mid, post) = match after0.find("{1}") {
Some(i) => (&after0[..i], &after0[i + 3..]),
None => (after0, ""),
};
(pre, mid, post)
}
fn spec(lang: &str) -> ListSpec {
use crate::cldr::list_spec;
let norm: String = lang
.chars()
.map(|c| {
if c == '_' {
'-'
} else {
c.to_ascii_lowercase()
}
})
.collect();
let mut end = norm.len();
loop {
if let Some(s) = list_spec(&norm[..end]) {
return s;
}
match norm[..end].rfind('-') {
Some(i) => end = i,
None => return list_spec("en").expect("root list spec present"),
}
}
}
#[must_use]
pub fn format_list(lang: &str, items: &[&str], style: ListStyle) -> String {
let s = spec(lang);
let p = match style {
ListStyle::And => s.and,
ListStyle::Or => s.or,
};
match items.len() {
0 => String::new(),
1 => items[0].to_string(),
2 => fmt2(p.two, items[0], items[1]),
n => {
let (pre_s, mid_s, post_s) = split2(p.start);
let (pre_m, mid_m, post_m) = split2(p.middle);
let (pre_e, mid_e, post_e) = split2(p.end);
let items_len: usize = items.iter().map(|s| s.len()).sum();
let cap = pre_e.len()
+ post_e.len()
+ mid_e.len()
+ pre_s.len()
+ mid_s.len()
+ post_s.len()
+ (pre_m.len() + mid_m.len() + post_m.len()) * n.saturating_sub(3)
+ items_len;
let mut out = String::with_capacity(cap);
out.push_str(pre_e);
for _ in 0..n - 3 {
out.push_str(pre_m);
}
out.push_str(pre_s);
out.push_str(items[0]);
out.push_str(mid_s);
out.push_str(items[1]);
out.push_str(post_s);
for item in &items[2..n - 1] {
out.push_str(mid_m);
out.push_str(item);
out.push_str(post_m);
}
out.push_str(mid_e);
out.push_str(items[n - 1]);
out.push_str(post_e);
out
}
}
}