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}