locale_types/
locale.rs

1/*!
2Provides a layer above the `LocaleString` for different locale specifiers.
3
4The `Locale` enum represents three forms of locale specification supported
5by the POSIX C API. These are:
6
71. The identifier "POSIX", or "C" is known as the _minimal locale_. It is
8   a rather neutral locale which has the same settings across all systems and
9   compilers, and therefore the exact results of a program using this locale
10   are predictable. This is the locale used by default on all C programs.
112. A path, starting with the '/' character and which resolves to a directory
12   containing the POSIX definition of a locale.
133. A locale string, represented in this crate as a `LocaleString` structure
14   that effectively represents a language with cultural qualification.
15
16The `Locale::from_str` method can be used to parse any of these kinds into
17the separate enumeration values.
18
19## Examples
20
21```
22use locale_types::{Locale,LocaleIdentifier};
23use std::str::FromStr;
24
25match Locale::from_str("C") {
26    Ok(Locale::POSIX) => (),
27    _ => panic!("could not parse first locale string")
28}
29
30let locale = Locale::from_str("en_US.UTF-8@Latn");
31if let Ok(Locale::String(locale_str)) = locale {
32    assert_eq!(locale_str.language_code(), "en".to_string());
33    assert_eq!(locale_str.territory().unwrap(), "US".to_string());
34    assert_eq!(locale_str.code_set().unwrap(), "UTF-8".to_string());
35    assert_eq!(locale_str.modifier().unwrap(), "Latn".to_string());
36} else {
37    panic!("could not parse second locale string")
38}
39```
40
41The following example is a more complete use case, the need to parse
42the commonly used `LC_ALL` environment variable to determine it's type
43and potential components.
44
45```
46use locale_types::Locale;
47use std::str::FromStr;
48use std::env;
49
50if let Ok(lc_str) = env::var("LC_ALL") {
51    match Locale::from_str(&lc_str) {
52        Ok(Locale::POSIX) =>
53            println!("POSIX minimal locale"),
54        Ok(Locale::Path(p)) =>
55            println!("Path locale"),
56        Ok(Locale::String(s)) =>
57            println!("String locale"),
58        _ => panic!("Parse Error"),
59    }
60}
61
62```
63*/
64
65use std::fmt;
66use std::fmt::Display;
67use std::path::PathBuf;
68use std::str::FromStr;
69
70use crate::string::{LocaleString, ParseError};
71
72// ------------------------------------------------------------------------------------------------
73// Public Types
74// ------------------------------------------------------------------------------------------------
75
76/// This enumeration represents the three types of Locale specifiers
77/// commonly used by operating systems.
78#[derive(Debug, PartialEq)]
79pub enum Locale {
80    /// The minimal locale specified by POSIX. Can be spoecified with
81    /// the string "POSIX" or simply "C".
82    POSIX,
83    /// A path to a locale specification, this library does not vslidste
84    /// whether the path exists, simply that it is a valid `PathBuf`..
85    Path(PathBuf),
86    /// A locale string, parsed into a structured `LocaleString` form.
87    String(LocaleString),
88}
89
90// ------------------------------------------------------------------------------------------------
91// Implementations - Locale
92// ------------------------------------------------------------------------------------------------
93
94const L_C: &'static str = "C";
95const L_POSIX: &'static str = "POSIX";
96const L_PATH_SEP: &'static str = "/";
97
98impl Display for Locale {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        write!(
101            f,
102            "{}",
103            match self {
104                Locale::POSIX => L_POSIX.to_string(),
105                Locale::Path(s) => s.to_str().unwrap().to_string(),
106                Locale::String(s) => s.to_string(),
107            }
108        )
109    }
110}
111
112impl FromStr for Locale {
113    type Err = ParseError;
114
115    fn from_str(s: &str) -> Result<Self, Self::Err> {
116        if s.len() == 0 {
117            return Err(ParseError::EmptyString);
118        }
119        match s {
120            L_C => Ok(Locale::POSIX),
121            L_POSIX => Ok(Locale::POSIX),
122            _ => {
123                if s.starts_with(L_PATH_SEP) {
124                    match PathBuf::from_str(s) {
125                        Ok(p) => Ok(Locale::Path(p)),
126                        Err(_) => Err(ParseError::InvalidPath),
127                    }
128                } else {
129                    Ok(Locale::String(LocaleString::from_str(s)?))
130                }
131            }
132        }
133    }
134}
135
136// ------------------------------------------------------------------------------------------------
137// Unit Tests
138// ------------------------------------------------------------------------------------------------
139
140#[cfg(test)]
141mod tests {
142    use crate::{Locale, LocaleIdentifier, LocaleString};
143    use std::path::PathBuf;
144    use std::str::FromStr;
145
146    // --------------------------------------------------------------------------------------------
147    #[test]
148    fn test_posix_to_string() {
149        assert_eq!(Locale::POSIX.to_string(), "POSIX");
150    }
151
152    #[test]
153    fn test_path_to_string() {
154        let _path = PathBuf::from_str("/usr/share/locale/en_US");
155        //        assert_eq!(path.to_string(), "/usr/share/locale/en_US");
156    }
157
158    #[test]
159    fn test_string_to_string() {
160        let locale = LocaleString::new("en".to_string())
161            .unwrap()
162            .with_territory("US".to_string())
163            .unwrap()
164            .with_code_set("UTF-8".to_string())
165            .unwrap();
166        assert_eq!(locale.to_string(), "en_US.UTF-8");
167    }
168
169    // --------------------------------------------------------------------------------------------
170    #[test]
171    fn test_posix_from_string() {
172        match Locale::from_str("POSIX") {
173            Ok(Locale::POSIX) => (),
174            _ => panic!("expecting Locale::POSIX"),
175        }
176        match Locale::from_str("C") {
177            Ok(Locale::POSIX) => (),
178            _ => panic!("expecting Locale::POSIX (C)"),
179        }
180    }
181
182    #[test]
183    fn test_path_from_string() {
184        match Locale::from_str("/usr/share/locale/en_US") {
185            Ok(Locale::Path(p)) => assert_eq!(p.to_str(), Some("/usr/share/locale/en_US")),
186            _ => panic!("expecting Locale::Path"),
187        }
188    }
189
190    #[test]
191    fn test_string_from_string() {
192        println!("{:#?}", Locale::from_str("en_US.UTF-8"));
193        match Locale::from_str("en_US.UTF-8") {
194            Ok(Locale::String(ls)) => {
195                assert_eq!(ls.language_code(), "en");
196                assert_eq!(ls.territory(), Some("US".to_string()));
197                assert_eq!(ls.code_set(), Some("UTF-8".to_string()));
198            }
199            _ => panic!("expecting Locale::String"),
200        }
201    }
202}