1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use std::fmt::{self, Display, Formatter};

/// Country code for a [`Language`] dialect
///
/// Uses <https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2>
#[non_exhaustive]
#[repr(u32)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Country {
    // FIXME: V2: u32::from_ne_bytes for country codes, with `\0` for unused
    // FIXME: Add aliases up to 3-4 letters, but hidden
    /// Any dialect
    Any,
    /// `US`: United States of America
    #[doc(hidden)]
    Us,
}

impl Display for Country {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.write_str(match self {
            Self::Any => "**",
            Self::Us => "US",
        })
    }
}

/// A spoken language
///
/// Use [`ToString::to_string()`] to convert to string of two letter lowercase
/// language code followed an forward slash and uppercase country code (example:
/// `en/US`).
///
/// Uses <https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes>
#[non_exhaustive]
#[derive(Clone, Eq, PartialEq, Debug)]
// #[allow(variant_size_differences)]
pub enum Language {
    #[doc(hidden)]
    __(Box<String>),
    /// `en`: English
    #[doc(hidden)]
    En(Country),
    /// `es`: Spanish
    #[doc(hidden)]
    Es(Country),
}

impl Language {
    /// Retrieve the country code for this language dialect.
    pub fn country(&self) -> Country {
        match self {
            Self::__(_) => Country::Any,
            Self::En(country) | Self::Es(country) => *country,
        }
    }
}

impl Display for Language {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::__(code) => f.write_str(code.as_str()),
            Self::En(country) => {
                if *country != Country::Any {
                    f.write_str("en/")?;
                    <Country as Display>::fmt(country, f)
                } else {
                    f.write_str("en")
                }
            }
            Self::Es(country) => {
                if *country != Country::Any {
                    f.write_str("es/")?;
                    <Country as Display>::fmt(country, f)
                } else {
                    f.write_str("es")
                }
            }
        }
    }
}