1/// The plural form resolution rule of the target language.
2pub 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
9impl 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)]
20pub struct PluralRulesError {
21 message: String,
22}
23
24impl From<&str> for PluralRulesError {
25 fn from(s: &str) -> Self {
26 Self {
27 message: s.to_string(),
28 }
29 }
30}
31
32impl From<String> for PluralRulesError {
33 fn from(s: String) -> Self {
34 Self { message: s }
35 }
36}
37
38impl 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
46impl 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
52impl std::error::Error for PluralRulesError {}
53
54impl 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