#![allow(dead_code)]
#[derive(Debug, Clone, Copy)]
pub struct Pattern {
pub prefix: &'static str,
pub suffix: &'static str,
pub min_int: u8,
pub min_frac: u8,
pub max_frac: u8,
pub primary_group: u8,
pub secondary_group: u8,
}
#[derive(Debug, Clone, Copy)]
pub struct NumberSpec {
pub decimal: &'static str,
pub group: &'static str,
pub minus: &'static str,
pub plus: &'static str,
pub percent: &'static str,
pub dec: Pattern,
pub pct: Pattern,
}
#[derive(Debug, Clone, Copy)]
pub struct ListPatterns {
pub start: &'static str,
pub middle: &'static str,
pub end: &'static str,
pub two: &'static str,
}
#[derive(Debug, Clone, Copy)]
pub struct ListSpec {
pub and: ListPatterns,
pub or: ListPatterns,
}
#[derive(Debug, Clone, Copy)]
pub struct RelUnit {
pub prev: Option<&'static str>,
pub cur: Option<&'static str>,
pub next: Option<&'static str>,
pub past: [Option<&'static str>; 6],
pub future: [Option<&'static str>; 6],
}
#[derive(Debug, Clone, Copy)]
pub struct CalendarSpec {
pub months_wide: [&'static str; 12],
pub months_abbr: [&'static str; 12],
pub days_wide: [&'static str; 7],
pub days_abbr: [&'static str; 7],
pub am: &'static str,
pub pm: &'static str,
pub date: [&'static str; 4],
pub time: [&'static str; 4],
pub datetime: [&'static str; 4],
}
#[derive(Debug, Clone, Copy)]
pub struct RelativeSpec {
pub units: [RelUnit; 7],
}
const NUMBERS: &[u8] = include_bytes!("cldr/numbers.bin");
const LISTS: &[u8] = include_bytes!("cldr/lists.bin");
const RELATIVE: &[u8] = include_bytes!("cldr/relative.bin");
const CURRENCY: &[u8] = include_bytes!("cldr/currency.bin");
const CURRENCY_DIGITS: &[u8] = include_bytes!("cldr/currency_digits.bin");
const DISPLAY_LANG: &[u8] = include_bytes!("cldr/display_languages.bin");
const DISPLAY_TERR: &[u8] = include_bytes!("cldr/display_territories.bin");
const UNITS: &[u8] = include_bytes!("cldr/units.bin");
const CALENDAR: &[u8] = include_bytes!("cldr/calendar.bin");
const SKELETONS: &[u8] = include_bytes!("cldr/skeletons.bin");
const LIKELY: &[u8] = include_bytes!("cldr/likely.bin");
const TIMEZONE: &[u8] = include_bytes!("cldr/timezone.bin");
const RBNF: &[u8] = include_bytes!("cldr/rbnf.bin");
const COMPACT: &[u8] = include_bytes!("cldr/compact.bin");
const NUMSYS_DIGITS: &[u8] = include_bytes!("cldr/numsys_digits.bin");
const NUMSYS_DEFAULT: &[u8] = include_bytes!("cldr/numsys_default.bin");
const ORDSUFFIX: &[u8] = include_bytes!("cldr/ordsuffix.bin");
#[cfg(feature = "collation")]
const COLLATION: &[u8] = include_bytes!("cldr/collation.bin");
#[cfg(feature = "collation")]
pub(crate) fn collation_rule(lang: &str) -> Option<&'static str> {
core::str::from_utf8(rbnf_like_payload(COLLATION, lang)?).ok()
}
#[cfg(feature = "collation")]
fn rbnf_like_payload(blob: &'static [u8], lang: &str) -> Option<&'static [u8]> {
let count = rd_u16(blob, 0);
let mut o = 2;
for _ in 0..count {
let klen = blob[o] as usize;
o += 1;
let k = &blob[o..o + klen];
o += klen;
let plen = rd_u16(blob, o);
o += 2;
if k == lang.as_bytes() {
return Some(&blob[o..o + plen]);
}
o += plen;
}
None
}
pub(crate) fn ordinal_suffix(lang: &str, category: usize) -> Option<&'static str> {
let mut c = find(ORDSUFFIX, lang)?;
let mut s = "";
for i in 0..6 {
let v = c.str();
if i == category {
s = v;
}
}
Some(s)
}
pub(crate) fn numbering_digits(system: &str) -> Option<&'static str> {
find(NUMSYS_DIGITS, system).map(|mut c| c.str())
}
pub(crate) fn default_numbering(lang: &str) -> Option<&'static str> {
find(NUMSYS_DEFAULT, lang).map(|mut c| c.str())
}
pub(crate) fn compact_patterns(lang: &str) -> Option<[&'static str; 12]> {
let mut c = find(COMPACT, lang)?;
Some(core::array::from_fn(|_| c.str()))
}
pub(crate) fn rbnf_payload(lang: &str) -> Option<&'static [u8]> {
let count = rd_u16(RBNF, 0);
let mut o = 2;
for _ in 0..count {
let klen = RBNF[o] as usize;
o += 1;
let k = &RBNF[o..o + klen];
o += klen;
let plen = rd_u16(RBNF, o);
o += 2;
if k == lang.as_bytes() {
return Some(&RBNF[o..o + plen]);
}
o += plen;
}
None
}
const ISLAMIC: &[u8] = include_bytes!("cldr/islamic.bin");
const PERSIAN: &[u8] = include_bytes!("cldr/persian.bin");
#[derive(Debug, Clone, Copy)]
pub struct AltCalSpec {
pub months_wide: [&'static str; 12],
pub months_abbr: [&'static str; 12],
pub era: &'static str,
pub date: [&'static str; 4],
}
fn alt_cal_spec(blob: &'static [u8], lang: &str) -> Option<AltCalSpec> {
let mut c = find(blob, lang)?;
Some(AltCalSpec {
months_wide: core::array::from_fn(|_| c.str()),
months_abbr: core::array::from_fn(|_| c.str()),
era: c.str(),
date: core::array::from_fn(|_| c.str()),
})
}
pub(crate) fn islamic_spec(lang: &str) -> Option<AltCalSpec> {
alt_cal_spec(ISLAMIC, lang)
}
pub(crate) fn persian_spec(lang: &str) -> Option<AltCalSpec> {
alt_cal_spec(PERSIAN, lang)
}
#[derive(Debug, Clone, Copy)]
pub struct TzSpec {
pub gmt: &'static str,
pub zero: &'static str,
pub hour: &'static str,
}
pub(crate) fn tz_spec(lang: &str) -> Option<TzSpec> {
let mut c = find(TIMEZONE, lang)?;
Some(TzSpec {
gmt: c.str(),
zero: c.str(),
hour: c.str(),
})
}
pub(crate) fn likely_subtags(key: &str) -> Option<&'static str> {
find(LIKELY, key).map(|mut c| c.str())
}
pub(crate) const UNIT_COUNT: usize = 28;
fn rd_u16(b: &[u8], o: usize) -> usize {
u16::from_le_bytes([b[o], b[o + 1]]) as usize
}
struct Cursor {
b: &'static [u8],
o: usize,
}
impl Cursor {
fn u8(&mut self) -> u8 {
let v = self.b[self.o];
self.o += 1;
v
}
fn u16(&mut self) -> usize {
let v = rd_u16(self.b, self.o);
self.o += 2;
v
}
fn str(&mut self) -> &'static str {
let n = self.u8() as usize;
let s = &self.b[self.o..self.o + n];
self.o += n;
core::str::from_utf8(s).unwrap_or("")
}
fn opt(&mut self) -> Option<&'static str> {
if self.b[self.o] == 0xFF {
self.o += 1;
None
} else {
Some(self.str())
}
}
fn skip_opt(&mut self) {
if self.b[self.o] == 0xFF {
self.o += 1;
} else {
let n = self.b[self.o] as usize;
self.o += 1 + n;
}
}
fn pattern(&mut self) -> Pattern {
Pattern {
prefix: self.str(),
suffix: self.str(),
min_int: self.u8(),
min_frac: self.u8(),
max_frac: self.u8(),
primary_group: self.u8(),
secondary_group: self.u8(),
}
}
}
fn find(blob: &'static [u8], key: &str) -> Option<Cursor> {
let count = rd_u16(blob, 0);
let mut o = 2;
for _ in 0..count {
let klen = blob[o] as usize;
o += 1;
let k = &blob[o..o + klen];
o += klen;
let plen = rd_u16(blob, o);
o += 2;
if k == key.as_bytes() {
return Some(Cursor { b: blob, o });
}
o += plen;
}
None
}
pub(crate) fn number_spec(lang: &str) -> Option<NumberSpec> {
let mut c = find(NUMBERS, lang)?;
Some(NumberSpec {
decimal: c.str(),
group: c.str(),
minus: c.str(),
plus: c.str(),
percent: c.str(),
dec: c.pattern(),
pct: c.pattern(),
})
}
fn list_patterns(c: &mut Cursor) -> ListPatterns {
ListPatterns {
start: c.str(),
middle: c.str(),
end: c.str(),
two: c.str(),
}
}
pub(crate) fn list_spec(lang: &str) -> Option<ListSpec> {
let mut c = find(LISTS, lang)?;
Some(ListSpec {
and: list_patterns(&mut c),
or: list_patterns(&mut c),
})
}
pub(crate) fn relative_spec(lang: &str) -> Option<RelativeSpec> {
let mut c = find(RELATIVE, lang)?;
let units = core::array::from_fn(|_| RelUnit {
prev: c.opt(),
cur: c.opt(),
next: c.opt(),
past: core::array::from_fn(|_| c.opt()),
future: core::array::from_fn(|_| c.opt()),
});
Some(RelativeSpec { units })
}
pub(crate) fn currency_pattern(lang: &str) -> Option<Pattern> {
let mut c = find(CURRENCY, lang)?;
Some(c.pattern())
}
pub(crate) fn currency_symbol(lang: &str, code: &str) -> Option<&'static str> {
let mut c = find(CURRENCY, lang)?;
let _ = c.pattern();
let n = c.u8();
for _ in 0..n {
let cd = c.str();
let sym = c.str();
if cd == code {
return Some(sym);
}
}
None
}
pub(crate) fn currency_digits(code: &str) -> u8 {
match find(CURRENCY_DIGITS, code) {
Some(mut c) => c.u8(),
None => 2,
}
}
fn display_name(blob: &'static [u8], display_locale: &str, code: &str) -> Option<&'static str> {
let mut c = find(blob, display_locale)?;
let count = c.u16();
for _ in 0..count {
let cd = c.str();
let name = c.str();
if cd == code {
return Some(name);
}
}
None
}
pub(crate) fn language_name(display_locale: &str, code: &str) -> Option<&'static str> {
display_name(DISPLAY_LANG, display_locale, code)
}
pub(crate) fn skeleton_pattern(lang: &str, skeleton: &str) -> Option<&'static str> {
display_name(SKELETONS, lang, skeleton)
}
pub(crate) fn region_name(display_locale: &str, code: &str) -> Option<&'static str> {
display_name(DISPLAY_TERR, display_locale, code)
}
pub(crate) fn calendar_spec(lang: &str) -> Option<CalendarSpec> {
let mut c = find(CALENDAR, lang)?;
Some(CalendarSpec {
months_wide: core::array::from_fn(|_| c.str()),
months_abbr: core::array::from_fn(|_| c.str()),
days_wide: core::array::from_fn(|_| c.str()),
days_abbr: core::array::from_fn(|_| c.str()),
am: c.str(),
pm: c.str(),
date: core::array::from_fn(|_| c.str()),
time: core::array::from_fn(|_| c.str()),
datetime: core::array::from_fn(|_| c.str()),
})
}
pub(crate) fn unit_pattern(
lang: &str,
width: usize,
unit: usize,
cat: usize,
) -> Option<&'static str> {
let mut c = find(UNITS, lang)?;
let base = (width * UNIT_COUNT + unit) * 6;
for _ in 0..base {
c.skip_opt();
}
let slots: [Option<&'static str>; 6] = core::array::from_fn(|_| c.opt());
slots[cat].or(slots[5])
}