#![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],
pub months_narrow: [&'static str; 12],
pub days_narrow: [&'static str; 7],
pub eras_wide: [&'static str; 2],
pub eras_abbr: [&'static str; 2],
pub eras_narrow: [&'static str; 2],
pub day_periods_wide: [Option<&'static str>; 10],
pub day_periods_abbr: [Option<&'static str>; 10],
pub day_periods_narrow: [Option<&'static str>; 10],
pub day_period_rules: &'static [u8],
}
impl CalendarSpec {
#[cfg(feature = "datetime")]
pub(crate) fn day_period_index(&self, minute: u16) -> u8 {
let b = self.day_period_rules;
let mut o = 0;
while o + 5 <= b.len() {
let from = u16::from_le_bytes([b[o], b[o + 1]]);
let to = u16::from_le_bytes([b[o + 2], b[o + 3]]);
let idx = b[o + 4];
o += 5;
if minute >= from && minute < to {
return idx;
}
}
0xFF
}
}
#[derive(Debug, Clone, Copy)]
pub struct RelativeSpec {
pub units: [RelUnit; 7],
}
#[cfg(feature = "number")]
const NUMBERS: &[u8] = include_bytes!("cldr/numbers.bin");
#[cfg(feature = "list")]
const LISTS: &[u8] = include_bytes!("cldr/lists.bin");
#[cfg(feature = "relative")]
const RELATIVE: &[u8] = include_bytes!("cldr/relative.bin");
#[cfg(feature = "currency")]
const CURRENCY: &[u8] = include_bytes!("cldr/currency.bin");
#[cfg(feature = "currency")]
const CURRENCY_DIGITS: &[u8] = include_bytes!("cldr/currency_digits.bin");
#[cfg(feature = "displaynames")]
const DISPLAY_LANG: &[u8] = include_bytes!("cldr/display_languages.bin");
#[cfg(feature = "displaynames")]
const DISPLAY_TERR: &[u8] = include_bytes!("cldr/display_territories.bin");
#[cfg(feature = "units")]
const UNITS: &[u8] = include_bytes!("cldr/units.bin");
#[cfg(feature = "datetime")]
const CALENDAR: &[u8] = include_bytes!("cldr/calendar.bin");
#[cfg(feature = "datetime")]
const SKELETONS: &[u8] = include_bytes!("cldr/skeletons.bin");
#[cfg(feature = "datetime")]
const INTERVALS: &[u8] = include_bytes!("cldr/intervals.bin");
#[cfg(feature = "locale")]
const LIKELY: &[u8] = include_bytes!("cldr/likely.bin");
#[cfg(feature = "locale")]
const ALIASES: &[u8] = include_bytes!("cldr/aliases.bin");
#[cfg(feature = "locale")]
const BCP47: &[u8] = include_bytes!("cldr/bcp47.bin");
#[cfg(feature = "datetime")]
const TIMEZONE: &[u8] = include_bytes!("cldr/timezone.bin");
#[cfg(feature = "spellout")]
const RBNF: &[u8] = include_bytes!("cldr/rbnf.bin");
#[cfg(feature = "number")]
const COMPACT: &[u8] = include_bytes!("cldr/compact.bin");
#[cfg(feature = "number")]
const NUMSYS_DIGITS: &[u8] = include_bytes!("cldr/numsys_digits.bin");
#[cfg(feature = "number")]
const NUMSYS_DEFAULT: &[u8] = include_bytes!("cldr/numsys_default.bin");
#[cfg(feature = "number")]
const ORDSUFFIX: &[u8] = include_bytes!("cldr/ordsuffix.bin");
#[cfg(feature = "collation")]
const COLLATION: &[u8] = include_bytes!("cldr/collation.bin");
#[cfg(feature = "calendars-extra")]
const ISLAMIC: &[u8] = include_bytes!("cldr/islamic.bin");
#[cfg(feature = "calendars-extra")]
const PERSIAN: &[u8] = include_bytes!("cldr/persian.bin");
#[cfg(feature = "calendars-extra")]
const CHINESE: &[u8] = include_bytes!("cldr/chinese.bin");
#[cfg(feature = "calendars-extra")]
const JAPANESE: &[u8] = include_bytes!("cldr/japanese.bin");
#[cfg(feature = "calendars-extra")]
const JAPANESE_HIST: &[u8] = include_bytes!("cldr/japanese_hist.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
}
#[cfg(feature = "number")]
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)
}
#[cfg(feature = "number")]
pub(crate) fn numbering_digits(system: &str) -> Option<&'static str> {
find(NUMSYS_DIGITS, system).map(|mut c| c.str())
}
#[cfg(feature = "number")]
pub(crate) fn default_numbering(lang: &str) -> Option<&'static str> {
find(NUMSYS_DEFAULT, lang).map(|mut c| c.str())
}
#[cfg(feature = "number")]
pub(crate) fn compact_patterns(lang: &str) -> Option<[&'static str; 24]> {
let mut c = find(COMPACT, lang)?;
Some(core::array::from_fn(|_| c.str()))
}
#[cfg(feature = "spellout")]
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
}
#[derive(Debug, Clone, Copy)]
pub struct AltCalSpec {
pub months_wide: [&'static str; 12],
pub months_abbr: [&'static str; 12],
pub eras_wide: [&'static str; 2],
pub eras_abbr: [&'static str; 2],
pub eras_narrow: [&'static str; 2],
pub date: [&'static str; 4],
}
#[cfg(feature = "calendars-extra")]
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()),
eras_wide: [c.str(), c.str()],
eras_abbr: [c.str(), c.str()],
eras_narrow: [c.str(), c.str()],
date: core::array::from_fn(|_| c.str()),
})
}
#[cfg(feature = "calendars-extra")]
pub(crate) fn islamic_spec(lang: &str) -> Option<AltCalSpec> {
alt_cal_spec(ISLAMIC, lang)
}
#[cfg(feature = "calendars-extra")]
pub(crate) fn persian_spec(lang: &str) -> Option<AltCalSpec> {
alt_cal_spec(PERSIAN, lang)
}
#[derive(Debug, Clone, Copy)]
pub struct ChineseCalSpec {
pub cyclic: [&'static str; 60],
pub months_wide: [&'static str; 12],
pub months_abbr: [&'static str; 12],
pub leap_wide: &'static str,
pub leap_abbr: &'static str,
pub date: [&'static str; 4],
}
#[cfg(feature = "calendars-extra")]
pub(crate) fn chinese_spec(lang: &str) -> Option<ChineseCalSpec> {
let mut c = find(CHINESE, lang)?;
Some(ChineseCalSpec {
cyclic: core::array::from_fn(|_| c.str()),
months_wide: core::array::from_fn(|_| c.str()),
months_abbr: core::array::from_fn(|_| c.str()),
leap_wide: c.str(),
leap_abbr: c.str(),
date: core::array::from_fn(|_| c.str()),
})
}
#[derive(Debug, Clone, Copy)]
pub struct JapaneseCalSpec {
pub eras_wide: [&'static str; 5],
pub eras_abbr: [&'static str; 5],
pub eras_narrow: [&'static str; 5],
pub date: [&'static str; 4],
pub gannen: u8,
}
#[cfg(feature = "calendars-extra")]
pub(crate) fn japanese_spec(lang: &str) -> Option<JapaneseCalSpec> {
let mut c = find(JAPANESE, lang)?;
Some(JapaneseCalSpec {
eras_wide: core::array::from_fn(|_| c.str()),
eras_abbr: core::array::from_fn(|_| c.str()),
eras_narrow: core::array::from_fn(|_| c.str()),
date: core::array::from_fn(|_| c.str()),
gannen: c.u8(),
})
}
#[cfg(feature = "calendars-extra")]
pub(crate) const HIST_ERA_COUNT: usize = 232;
#[cfg(feature = "calendars-extra")]
pub(crate) fn japanese_hist_eras(lang: &str, index: usize) -> Option<[&'static str; 3]> {
if index >= HIST_ERA_COUNT {
return None;
}
let b = JAPANESE_HIST;
let nloc = rd_u16(b, 0);
let mut o = 2;
let mut setid: Option<usize> = None;
for _ in 0..nloc {
let klen = b[o] as usize;
o += 1;
let k = &b[o..o + klen];
o += klen;
let sid = rd_u16(b, o);
o += 2;
if k == lang.as_bytes() {
setid = Some(sid);
}
}
let setid = setid?;
let nsets = rd_u16(b, o);
o += 2;
let table = o;
let sets_base = table + nsets * 4;
let set_off = rd_u32(b, table + setid * 4);
let mut c = Cursor {
b,
o: sets_base + set_off,
};
let mut res = [""; 3];
for slot in &mut res {
for i in 0..HIST_ERA_COUNT {
let s = c.str();
if i == index {
*slot = s;
}
}
}
Some(res)
}
#[derive(Debug, Clone, Copy)]
pub struct TzSpec {
pub gmt: &'static str,
pub zero: &'static str,
pub hour: &'static str,
}
#[cfg(feature = "datetime")]
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(),
})
}
#[cfg(feature = "locale")]
pub(crate) fn likely_subtags(key: &str) -> Option<&'static str> {
find(LIKELY, key).map(|mut c| c.str())
}
#[cfg(feature = "locale")]
pub(crate) fn alias_lookup(prefixed_key: &str) -> Option<&'static str> {
find(ALIASES, prefixed_key).map(|mut c| c.str())
}
#[cfg(feature = "locale")]
pub(crate) fn bcp47_type_alias(keyword_and_value: &str) -> Option<&'static str> {
find(BCP47, keyword_and_value).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
}
#[cfg(feature = "calendars-extra")]
fn rd_u32(b: &[u8], o: usize) -> usize {
u32::from_le_bytes([b[o], b[o + 1], b[o + 2], b[o + 3]]) 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 dp_rules(&mut self) -> &'static [u8] {
let n = self.u8() as usize * 5;
let s = &self.b[self.o..self.o + n];
self.o += n;
s
}
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
}
#[cfg(feature = "number")]
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(),
}
}
#[cfg(feature = "list")]
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),
})
}
#[cfg(feature = "relative")]
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 })
}
#[cfg(feature = "currency")]
pub(crate) fn currency_pattern(lang: &str) -> Option<Pattern> {
let mut c = find(CURRENCY, lang)?;
Some(c.pattern())
}
#[cfg(feature = "currency")]
pub(crate) fn currency_unit_pattern(lang: &str) -> Option<&'static str> {
let mut c = find(CURRENCY, lang)?;
let _ = c.pattern();
Some(c.str())
}
#[cfg(feature = "currency")]
pub(crate) fn currency_forms(
lang: &str,
code: &str,
) -> Option<(&'static str, &'static str, &'static str)> {
let mut c = find(CURRENCY, lang)?;
let _ = c.pattern();
let _ = c.str(); let n = c.u16();
for _ in 0..n {
let cd = c.str();
let sym = c.str();
let narrow = c.str();
let name = c.str();
if cd == code {
return Some((sym, narrow, name));
}
}
None
}
#[cfg(feature = "currency")]
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
}
#[cfg(feature = "displaynames")]
pub(crate) fn language_name(display_locale: &str, code: &str) -> Option<&'static str> {
display_name(DISPLAY_LANG, display_locale, code)
}
#[cfg(feature = "datetime")]
pub(crate) fn skeleton_pattern(lang: &str, skeleton: &str) -> Option<&'static str> {
display_name(SKELETONS, lang, skeleton)
}
#[cfg(feature = "datetime")]
pub(crate) fn interval_fallback(lang: &str) -> Option<&'static str> {
find(INTERVALS, lang).map(|mut c| c.str())
}
#[cfg(feature = "datetime")]
pub(crate) fn interval_pattern(lang: &str, skeleton: &str, field: u8) -> Option<&'static str> {
let mut c = find(INTERVALS, lang)?;
let _fallback = c.str();
let count = c.u16();
for _ in 0..count {
let sk = c.str();
let fcount = c.u8();
let mut found: Option<&'static str> = None;
for _ in 0..fcount {
let letter = c.u8();
let pat = c.str();
if sk == skeleton && letter == field {
found = Some(pat);
}
}
if sk == skeleton {
return found;
}
}
None
}
#[cfg(feature = "displaynames")]
pub(crate) fn region_name(display_locale: &str, code: &str) -> Option<&'static str> {
display_name(DISPLAY_TERR, display_locale, code)
}
#[cfg(feature = "datetime")]
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()),
months_narrow: core::array::from_fn(|_| c.str()),
days_narrow: core::array::from_fn(|_| c.str()),
eras_wide: core::array::from_fn(|_| c.str()),
eras_abbr: core::array::from_fn(|_| c.str()),
eras_narrow: core::array::from_fn(|_| c.str()),
day_periods_wide: core::array::from_fn(|_| c.opt()),
day_periods_abbr: core::array::from_fn(|_| c.opt()),
day_periods_narrow: core::array::from_fn(|_| c.opt()),
day_period_rules: c.dp_rules(),
})
}
#[cfg(feature = "units")]
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])
}