1 | /// The plural form resolution rule of the target language. |
2 | pub struct CatalogPluralRules { |
3 | /// Total number of plural forms, including singular form. |
4 | pub nplurals: usize, |
5 | /// The plural form resolution expression in the function of n. |
6 | pub expr: String, |
7 | } |
8 | |
9 | impl Default for CatalogPluralRules { |
10 | fn default() -> Self { |
11 | Self { |
12 | nplurals: 1, |
13 | expr: String::from("0" ), |
14 | } |
15 | } |
16 | } |
17 | |
18 | /// Error type when parsing an invalid plural rules. |
19 | #[derive (Debug)] |
20 | pub struct PluralRulesError { |
21 | message: String, |
22 | } |
23 | |
24 | impl From<&str> for PluralRulesError { |
25 | fn from(s: &str) -> Self { |
26 | Self { |
27 | message: s.to_string(), |
28 | } |
29 | } |
30 | } |
31 | |
32 | impl From<String> for PluralRulesError { |
33 | fn from(s: String) -> Self { |
34 | Self { message: s } |
35 | } |
36 | } |
37 | |
38 | impl From<std::num::ParseIntError> for PluralRulesError { |
39 | fn from(value: std::num::ParseIntError) -> Self { |
40 | Self { |
41 | message: format!("cannot parse nplurals: {}" , value), |
42 | } |
43 | } |
44 | } |
45 | |
46 | impl std::fmt::Display for PluralRulesError { |
47 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
48 | write!(f, "invalid plural rules: {}" , self.message) |
49 | } |
50 | } |
51 | |
52 | impl std::error::Error for PluralRulesError {} |
53 | |
54 | impl CatalogPluralRules { |
55 | /// Parse a plural resolution rules from string form stored in PO file. |
56 | pub fn parse(rules: &str) -> Result<Self, PluralRulesError> { |
57 | let mut nplurals: Option<usize> = None; |
58 | let mut expr: Option<&str> = None; |
59 | for rule in rules.split(';' ) { |
60 | let rule = rule.trim(); |
61 | if rule.is_empty() { |
62 | continue; |
63 | } |
64 | if let Some((key, value)) = rule.split_once('=' ) { |
65 | match key { |
66 | "nplurals" => { |
67 | nplurals = Some(value.parse()?); |
68 | } |
69 | "plural" => { |
70 | expr = Some(value); |
71 | } |
72 | unrecognized => { |
73 | return Err(PluralRulesError::from(format!( |
74 | "unrecognized entry {}" , |
75 | unrecognized |
76 | ))); |
77 | } |
78 | } |
79 | } else { |
80 | return Err(PluralRulesError::from(format!("cannot parse {}" , rule))); |
81 | } |
82 | } |
83 | if let (Some(nplurals), Some(expr)) = (nplurals, expr) { |
84 | if nplurals == 0 { |
85 | Err(PluralRulesError::from("nplurals equals to zero" )) |
86 | } else if expr.is_empty() { |
87 | Err(PluralRulesError::from("plural rule expression is empty" )) |
88 | } else { |
89 | Ok(CatalogPluralRules { |
90 | nplurals, |
91 | expr: String::from(expr), |
92 | }) |
93 | } |
94 | } else if nplurals.is_none() { |
95 | Err(PluralRulesError::from("nplurals does not exist" )) |
96 | } else if expr.is_none() { |
97 | Err(PluralRulesError::from( |
98 | "plural rule expression does not exist" , |
99 | )) |
100 | } else { |
101 | std::unreachable!(); |
102 | } |
103 | } |
104 | |
105 | /// Dump the plural resolution rules to string form to write to a PO file. |
106 | pub fn dump(&self) -> String { |
107 | format!("nplurals= {}; plural= {};" , self.nplurals, self.expr) |
108 | } |
109 | } |
110 | |