icu_plurals/provider/rules/reference/
serializer.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use crate::provider::rules::reference::ast;
6use core::fmt;
7use core::ops::RangeInclusive;
8
9/// Unicode Plural Rule serializer converts an [`AST`] to a [`String`].
10///
11/// <div class="stab unstable">
12/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
13/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
14/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
15/// </div>
16///
17/// # Examples
18///
19/// ```
20/// use icu::plurals::provider::rules::reference::ast;
21/// use icu::plurals::provider::rules::reference::parse;
22/// use icu::plurals::provider::rules::reference::serialize;
23///
24/// let input = "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04";
25///
26/// let ast = parse(input.as_bytes()).expect("Parsing failed.");
27///
28/// assert_eq!(ast.condition.0[0].0[0].expression.operand, ast::Operand::I);
29/// assert_eq!(ast.condition.0[1].0[0].expression.operand, ast::Operand::N);
30///
31/// let mut result = String::new();
32/// serialize(&ast, &mut result).expect("Serialization failed.");
33///
34/// assert_eq!(input, result);
35/// ```
36///
37/// [`AST`]: crate::provider::rules::reference::ast
38pub fn serialize(rule: &ast::Rule, w: &mut impl fmt::Write) -> fmt::Result {
39    serialize_condition(&rule.condition, w)?;
40    if let Some(samples) = &rule.samples {
41        serialize_samples(samples, w)?;
42    }
43    Ok(())
44}
45
46/// <div class="stab unstable">
47/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
48/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
49/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
50/// </div>
51pub fn serialize_condition(cond: &ast::Condition, w: &mut impl fmt::Write) -> fmt::Result {
52    let mut first = true;
53
54    for cond in cond.0.iter() {
55        if first {
56            first = false;
57        } else {
58            w.write_str(" or ")?;
59        }
60        serialize_andcondition(cond, w)?;
61    }
62    Ok(())
63}
64
65fn serialize_andcondition(cond: &ast::AndCondition, w: &mut impl fmt::Write) -> fmt::Result {
66    let mut first = true;
67
68    for relation in cond.0.iter() {
69        if first {
70            first = false;
71        } else {
72            w.write_str(" and ")?;
73        }
74        serialize_relation(relation, w)?;
75    }
76    Ok(())
77}
78
79fn serialize_relation(relation: &ast::Relation, w: &mut impl fmt::Write) -> fmt::Result {
80    serialize_expression(&relation.expression, w)?;
81    w.write_char(' ')?;
82    serialize_operator(relation.operator, w)?;
83    w.write_char(' ')?;
84    serialize_rangelist(&relation.range_list, w)
85}
86
87fn serialize_expression(exp: &ast::Expression, w: &mut impl fmt::Write) -> fmt::Result {
88    serialize_operand(exp.operand, w)?;
89    if let Some(modulus) = &exp.modulus {
90        w.write_str(" % ")?;
91        serialize_value(modulus, w)?;
92    }
93    Ok(())
94}
95
96fn serialize_operator(operator: ast::Operator, w: &mut impl fmt::Write) -> fmt::Result {
97    match operator {
98        ast::Operator::Eq => w.write_char('='),
99        ast::Operator::NotEq => w.write_str("!="),
100    }
101}
102
103fn serialize_operand(operand: ast::Operand, w: &mut impl fmt::Write) -> fmt::Result {
104    match operand {
105        ast::Operand::N => w.write_char('n'),
106        ast::Operand::I => w.write_char('i'),
107        ast::Operand::V => w.write_char('v'),
108        ast::Operand::W => w.write_char('w'),
109        ast::Operand::F => w.write_char('f'),
110        ast::Operand::T => w.write_char('t'),
111        ast::Operand::C => w.write_char('c'),
112        ast::Operand::E => w.write_char('e'),
113    }
114}
115
116fn serialize_rangelist(rl: &ast::RangeList, w: &mut impl fmt::Write) -> fmt::Result {
117    let mut first = true;
118
119    for rli in rl.0.iter() {
120        if first {
121            first = false;
122        } else {
123            w.write_str(", ")?;
124        }
125        serialize_rangelistitem(rli, w)?
126    }
127    Ok(())
128}
129
130fn serialize_rangelistitem(rli: &ast::RangeListItem, w: &mut impl fmt::Write) -> fmt::Result {
131    match rli {
132        ast::RangeListItem::Range(range) => serialize_range(range, w),
133        ast::RangeListItem::Value(v) => serialize_value(v, w),
134    }
135}
136
137fn serialize_range(range: &RangeInclusive<ast::Value>, w: &mut impl fmt::Write) -> fmt::Result {
138    serialize_value(range.start(), w)?;
139    w.write_str("..")?;
140    serialize_value(range.end(), w)?;
141    Ok(())
142}
143
144fn serialize_value(value: &ast::Value, w: &mut impl fmt::Write) -> fmt::Result {
145    write!(w, "{}", value.0)
146}
147
148/// <div class="stab unstable">
149/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
150/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
151/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
152/// </div>
153pub fn serialize_samples(samples: &ast::Samples, w: &mut impl fmt::Write) -> fmt::Result {
154    if let Some(sample_list) = &samples.integer {
155        w.write_str(" @integer ")?;
156        serialize_sample_list(sample_list, w)?;
157    }
158    if let Some(sample_list) = &samples.decimal {
159        w.write_str(" @decimal ")?;
160        serialize_sample_list(sample_list, w)?;
161    }
162    Ok(())
163}
164
165/// <div class="stab unstable">
166/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
167/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
168/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
169/// </div>
170pub fn serialize_sample_list(samples: &ast::SampleList, w: &mut impl fmt::Write) -> fmt::Result {
171    let mut first = true;
172
173    for sample_range in samples.sample_ranges.iter() {
174        if first {
175            first = false;
176        } else {
177            w.write_str(", ")?;
178        }
179        serialize_sample_range(sample_range, w)?;
180    }
181
182    if samples.ellipsis {
183        w.write_str(", …")?;
184    }
185    Ok(())
186}
187
188/// <div class="stab unstable">
189/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
190/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
191/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
192/// </div>
193pub fn serialize_sample_range(
194    sample_range: &ast::SampleRange,
195    w: &mut impl fmt::Write,
196) -> fmt::Result {
197    serialize_decimal_value(&sample_range.lower_val, w)?;
198    if let Some(upper_val) = &sample_range.upper_val {
199        w.write_char('~')?;
200        serialize_decimal_value(upper_val, w)?;
201    }
202    Ok(())
203}
204
205/// <div class="stab unstable">
206/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
207/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
208/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
209/// </div>
210pub fn serialize_decimal_value(val: &ast::DecimalValue, w: &mut impl fmt::Write) -> fmt::Result {
211    w.write_str(&val.0)
212}