#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Weekday {
Monday,
Sunday,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Locale {
pub decimal: char,
pub grouping: Option<char>,
pub month_short: [&'static str; 12],
pub month_long: [&'static str; 12],
pub day_short: [&'static str; 7],
pub day_long: [&'static str; 7],
pub am: &'static str,
pub pm: &'static str,
pub first_dow: Weekday,
}
impl Locale {
pub const EN_US: Locale = Locale {
decimal: '.',
grouping: Some(','),
month_short: [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
],
month_long: [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
],
day_short: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
day_long: [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
],
am: "AM",
pm: "PM",
first_dow: Weekday::Sunday,
};
pub const DE_DE: Locale = Locale {
decimal: ',',
grouping: Some('.'),
month_short: [
"Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez",
],
month_long: [
"Januar",
"Februar",
"März",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember",
],
day_short: ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"],
day_long: [
"Montag",
"Dienstag",
"Mittwoch",
"Donnerstag",
"Freitag",
"Samstag",
"Sonntag",
],
am: "vorm.",
pm: "nachm.",
first_dow: Weekday::Monday,
};
pub const FR_FR: Locale = Locale {
decimal: ',',
grouping: Some(' '),
month_short: [
"janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
"nov.", "déc.",
],
month_long: [
"janvier",
"février",
"mars",
"avril",
"mai",
"juin",
"juillet",
"août",
"septembre",
"octobre",
"novembre",
"décembre",
],
day_short: ["lun.", "mar.", "mer.", "jeu.", "ven.", "sam.", "dim."],
day_long: [
"lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche",
],
am: "AM",
pm: "PM",
first_dow: Weekday::Monday,
};
}
impl Default for Locale {
fn default() -> Self {
Self::EN_US
}
}
#[cfg(test)]
mod tests {
use super::*;
const BUILT_INS: [(&str, Locale); 3] = [
("EN_US", Locale::EN_US),
("DE_DE", Locale::DE_DE),
("FR_FR", Locale::FR_FR),
];
fn undotted(s: &str) -> &str {
s.strip_suffix('.').unwrap_or(s)
}
#[test]
fn default_locale_is_us_english() {
assert_eq!(Locale::default(), Locale::EN_US);
}
#[test]
fn separators_never_collide_within_a_locale() {
for (name, loc) in BUILT_INS {
if let Some(grouping) = loc.grouping {
assert_ne!(
grouping, loc.decimal,
"{name} uses '{grouping}' for both grouping and decimal"
);
}
}
}
#[test]
fn calendar_names_are_populated_and_distinct() {
for (name, loc) in BUILT_INS {
for (label, names) in [
("month_short", &loc.month_short[..]),
("month_long", &loc.month_long[..]),
("day_short", &loc.day_short[..]),
("day_long", &loc.day_long[..]),
] {
for entry in names {
assert!(!entry.trim().is_empty(), "{name}.{label} has a blank entry");
}
let mut seen: Vec<&str> = names.to_vec();
seen.sort_unstable();
let before = seen.len();
seen.dedup();
assert_eq!(
seen.len(),
before,
"{name}.{label} repeats a name — the table is misaligned"
);
}
}
}
#[test]
fn short_calendar_names_abbreviate_their_long_counterparts() {
for (name, loc) in BUILT_INS {
for i in 0..12 {
let (short, long) = (undotted(loc.month_short[i]), loc.month_long[i]);
assert!(
long.to_lowercase().starts_with(&short.to_lowercase()),
"{name}: month {i} short \"{short}\" does not abbreviate \"{long}\""
);
}
for i in 0..7 {
let (short, long) = (undotted(loc.day_short[i]), loc.day_long[i]);
assert!(
long.to_lowercase().starts_with(&short.to_lowercase()),
"{name}: day {i} short \"{short}\" does not abbreviate \"{long}\""
);
}
}
}
#[test]
fn day_tables_run_monday_first_independently_of_the_first_day_of_week() {
assert_eq!(Locale::EN_US.day_long[0], "Monday");
assert_eq!(Locale::EN_US.day_long[6], "Sunday");
assert_eq!(Locale::DE_DE.day_long[0], "Montag");
assert_eq!(Locale::FR_FR.day_long[0], "lundi");
assert_eq!(Locale::EN_US.first_dow, Weekday::Sunday);
assert_eq!(Locale::DE_DE.first_dow, Weekday::Monday);
}
}