num2words2_core/
presentation.rs1pub fn resolve_lang(raw: &str) -> Option<String> {
21 let keys = crate::supported_lang_keys();
22 #[allow(clippy::manual_contains)]
25 let known = |s: &str| keys.iter().any(|k| *k == s);
26
27 if known(raw) {
28 return Some(raw.to_string());
29 }
30 let nl = raw.replace('-', "_");
31 if known(&nl) {
32 return Some(nl);
33 }
34 let parts: Vec<&str> = nl.split('_').collect();
35 if parts.len() >= 2 {
36 let candidate = format!("{}_{}", parts[0].to_lowercase(), parts[1].to_uppercase());
37 if known(&candidate) {
38 return Some(candidate);
39 }
40 if known(parts[0]) {
41 return Some(parts[0].to_string());
42 }
43 }
44 let prefix: String = nl.chars().take(2).collect();
45 if known(&prefix) {
46 return Some(prefix);
47 }
48 None
49}
50
51pub fn apply_style(result: &str, style: Option<&str>, to: &str, lang: &str) -> String {
59 let mut out = result.to_string();
60 if style == Some("terse") && to == "ordinal" {
61 for prefix in ["one ", "un ", "uno "] {
62 if out.starts_with(prefix) && out.len() > prefix.len() {
63 out = out[prefix.len()..].to_string();
64 break;
65 }
66 }
67 }
68 if style == Some("us") && lang.starts_with("en") {
69 out = out.replace(" and ", " ");
70 }
71 out
72}
73
74pub enum CentsArg<'a> {
76 Absent,
78 Bool(bool),
80 Str(&'a str),
82 Other,
85}
86
87pub fn normalize_cents(cents: CentsArg) -> Option<(bool, bool)> {
97 match cents {
98 CentsArg::Absent => Some((true, false)),
99 CentsArg::Str("omit") => Some((true, true)),
100 CentsArg::Str("verbose") => Some((true, false)),
101 CentsArg::Str("terse") => Some((false, false)),
102 CentsArg::Bool(b) => Some((b, false)),
103 CentsArg::Str(_) | CentsArg::Other => None,
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn resolve_exact_and_hyphen() {
113 assert_eq!(resolve_lang("en").as_deref(), Some("en"));
114 assert_eq!(resolve_lang("pt_BR").as_deref(), Some("pt_BR"));
115 assert_eq!(resolve_lang("pt-BR").as_deref(), Some("pt_BR"));
117 }
118
119 #[test]
120 fn resolve_casing_and_prefix() {
121 assert_eq!(resolve_lang("en-US").as_deref(), Some("en"));
123 assert_eq!(resolve_lang("en_US").as_deref(), Some("en"));
124 assert_eq!(resolve_lang("english").as_deref(), Some("en"));
126 }
127
128 #[test]
129 fn resolve_unknown() {
130 assert_eq!(resolve_lang("unknown_lang"), None);
131 assert_eq!(resolve_lang("zz"), None);
132 }
133
134 #[test]
135 fn style_terse_strips_leading_one() {
136 assert_eq!(
137 apply_style("one hundredth", Some("terse"), "ordinal", "en"),
138 "hundredth"
139 );
140 assert_eq!(apply_style("first", Some("terse"), "ordinal", "en"), "first");
142 assert_eq!(
144 apply_style("one hundred", Some("terse"), "cardinal", "en"),
145 "one hundred"
146 );
147 }
148
149 #[test]
150 fn style_us_drops_and() {
151 assert_eq!(
152 apply_style("one thousand and one", Some("us"), "cardinal", "en"),
153 "one thousand one"
154 );
155 assert_eq!(
157 apply_style("mil y uno", Some("us"), "cardinal", "es"),
158 "mil y uno"
159 );
160 assert_eq!(apply_style("x and y", None, "cardinal", "en"), "x and y");
162 }
163
164 #[test]
165 fn cents_modes() {
166 assert_eq!(normalize_cents(CentsArg::Absent), Some((true, false)));
167 assert_eq!(normalize_cents(CentsArg::Str("omit")), Some((true, true)));
168 assert_eq!(normalize_cents(CentsArg::Str("verbose")), Some((true, false)));
169 assert_eq!(normalize_cents(CentsArg::Str("terse")), Some((false, false)));
170 assert_eq!(normalize_cents(CentsArg::Bool(true)), Some((true, false)));
171 assert_eq!(normalize_cents(CentsArg::Bool(false)), Some((false, false)));
172 assert_eq!(normalize_cents(CentsArg::Str("nope")), None);
173 assert_eq!(normalize_cents(CentsArg::Other), None);
174 }
175}