]> git.proxmox.com Git - rustc.git/blob - vendor/icu_list/src/list_formatter.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / vendor / icu_list / src / list_formatter.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
5 use crate::provider::{AndListV1Marker, ErasedListV1Marker, OrListV1Marker, UnitListV1Marker};
6 use crate::ListError;
7 use crate::ListLength;
8 use core::fmt::{self, Write};
9 use icu_provider::prelude::*;
10 use writeable::*;
11
12 /// A formatter that renders sequences of items in an i18n-friendly way. See the
13 /// [crate-level documentation](crate) for more details.
14 pub struct ListFormatter {
15 data: DataPayload<ErasedListV1Marker>,
16 length: ListLength,
17 }
18
19 macro_rules! constructor {
20 ($name: ident, $name_any: ident, $name_buffer: ident, $marker: ty, $doc: literal) => {
21 #[doc = concat!("Creates a new [`ListFormatter`] that produces a ", $doc, "-type list.\n\nSee the [CLDR spec]",
22 "(https://unicode.org/reports/tr35/tr35-general.html#ListPatterns) for an explanation of the different types.\n\n",
23 "[📚 Help choosing a constructor](icu_provider::constructors)\n\n",
24 "<div class=\"stab unstable\">⚠️ The bounds on this function may change over time, including in SemVer minor releases.</div>")]
25 pub fn $name<D: DataProvider<$marker> + ?Sized>(
26 data_provider: &D,
27 locale: &DataLocale,
28 length: ListLength,
29 ) -> Result<Self, ListError> {
30 let data = data_provider
31 .load(DataRequest {
32 locale,
33 metadata: Default::default(),
34 })?
35 .take_payload()?.cast();
36 Ok(Self { data, length })
37 }
38 icu_provider::gen_any_buffer_constructors!(
39 locale: include,
40 style: ListLength,
41 error: ListError,
42 functions: [
43 Self::$name,
44 $name_any,
45 $name_buffer
46 ]
47 );
48 };
49 }
50
51 impl ListFormatter {
52 constructor!(
53 try_new_and_with_length_unstable,
54 try_new_and_with_length_with_any_provider,
55 try_new_and_with_length_with_buffer_provider,
56 AndListV1Marker,
57 "and"
58 );
59 constructor!(
60 try_new_or_with_length_unstable,
61 try_new_or_with_length_with_any_provider,
62 try_new_or_with_length_with_buffer_provider,
63 OrListV1Marker,
64 "or"
65 );
66 constructor!(
67 try_new_unit_with_length_unstable,
68 try_new_unit_with_length_with_any_provider,
69 try_new_unit_with_length_with_buffer_provider,
70 UnitListV1Marker,
71 "unit"
72 );
73
74 /// Returns a [`Writeable`] composed of the input [`Writeable`]s and the language-dependent
75 /// formatting. The first layer of parts contains [`parts::ELEMENT`] for input
76 /// elements, and [`parts::LITERAL`] for list literals.
77 pub fn format<'a, W: Writeable + 'a, I: Iterator<Item = W> + Clone + 'a>(
78 &'a self,
79 values: I,
80 ) -> FormattedList<'a, W, I> {
81 FormattedList {
82 formatter: self,
83 values,
84 }
85 }
86
87 /// Returns a [`String`] composed of the input [`Writeable`]s and the language-dependent
88 /// formatting.
89 pub fn format_to_string<W: Writeable, I: Iterator<Item = W> + Clone>(
90 &self,
91 values: I,
92 ) -> alloc::string::String {
93 self.format(values).write_to_string().into_owned()
94 }
95 }
96
97 /// The [`Part`]s used by [`ListFormatter`].
98 pub mod parts {
99 use writeable::Part;
100
101 /// The [`Part`] used by [`FormattedList`](super::FormattedList) to mark the part of the string that is an element.
102 pub const ELEMENT: Part = Part {
103 category: "list",
104 value: "element",
105 };
106
107 /// The [`Part`] used by [`FormattedList`](super::FormattedList) to mark the part of the string that is a list literal,
108 /// such as ", " or " and ".
109 pub const LITERAL: Part = Part {
110 category: "list",
111 value: "literal",
112 };
113 }
114
115 /// The [`Writeable`] implementation that is returned by [`ListFormatter::format`]. See
116 /// the [`writeable`] crate for how to consume this.
117 pub struct FormattedList<'a, W: Writeable + 'a, I: Iterator<Item = W> + Clone + 'a> {
118 formatter: &'a ListFormatter,
119 values: I,
120 }
121
122 impl<'a, W: Writeable + 'a, I: Iterator<Item = W> + Clone + 'a> Writeable
123 for FormattedList<'a, W, I>
124 {
125 fn write_to_parts<V: PartsWrite + ?Sized>(&self, sink: &mut V) -> fmt::Result {
126 macro_rules! literal {
127 ($lit:ident) => {
128 sink.with_part(parts::LITERAL, |l| l.write_str($lit))
129 };
130 }
131 macro_rules! value {
132 ($val:expr) => {
133 sink.with_part(parts::ELEMENT, |e| $val.write_to_parts(e))
134 };
135 }
136
137 let mut values = self.values.clone();
138
139 if let Some(first) = values.next() {
140 if let Some(second) = values.next() {
141 if let Some(third) = values.next() {
142 // Start(values[0], middle(..., middle(values[n-3], End(values[n-2], values[n-1]))...)) =
143 // start_before + values[0] + start_between + (values[1..n-3] + middle_between)* +
144 // values[n-2] + end_between + values[n-1] + end_after
145
146 let (start_before, start_between, _) = self
147 .formatter
148 .data
149 .get()
150 .start(self.formatter.length)
151 .parts(&second);
152
153 literal!(start_before)?;
154 value!(first)?;
155 literal!(start_between)?;
156 value!(second)?;
157
158 let mut next = third;
159
160 for next_next in values {
161 let (_, between, _) = self
162 .formatter
163 .data
164 .get()
165 .middle(self.formatter.length)
166 .parts(&next);
167 literal!(between)?;
168 value!(next)?;
169 next = next_next;
170 }
171
172 let (_, end_between, end_after) = self
173 .formatter
174 .data
175 .get()
176 .end(self.formatter.length)
177 .parts(&next);
178 literal!(end_between)?;
179 value!(next)?;
180 literal!(end_after)
181 } else {
182 // Pair(values[0], values[1]) = pair_before + values[0] + pair_between + values[1] + pair_after
183 let (before, between, after) = self
184 .formatter
185 .data
186 .get()
187 .pair(self.formatter.length)
188 .parts(&second);
189 literal!(before)?;
190 value!(first)?;
191 literal!(between)?;
192 value!(second)?;
193 literal!(after)
194 }
195 } else {
196 value!(first)
197 }
198 } else {
199 Ok(())
200 }
201 }
202
203 fn writeable_length_hint(&self) -> LengthHint {
204 let mut count = 0;
205 let item_length = self
206 .values
207 .clone()
208 .map(|w| {
209 count += 1;
210 w.writeable_length_hint()
211 })
212 .sum::<LengthHint>();
213 item_length
214 + self
215 .formatter
216 .data
217 .get()
218 .size_hint(self.formatter.length, count)
219 }
220 }
221
222 impl<'a, W: Writeable + 'a, I: Iterator<Item = W> + Clone + 'a> core::fmt::Display
223 for FormattedList<'a, W, I>
224 {
225 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
226 self.write_to(f)
227 }
228 }
229
230 #[cfg(all(test, feature = "datagen"))]
231 mod tests {
232 use super::*;
233 use writeable::{assert_writeable_eq, assert_writeable_parts_eq};
234
235 fn formatter(length: ListLength) -> ListFormatter {
236 ListFormatter {
237 data: DataPayload::from_owned(crate::provider::test::test_patterns()),
238 length,
239 }
240 }
241
242 #[test]
243 fn test_slices() {
244 let formatter = formatter(ListLength::Wide);
245 let values = ["one", "two", "three", "four", "five"];
246
247 assert_writeable_eq!(formatter.format(values[0..0].iter()), "");
248 assert_writeable_eq!(formatter.format(values[0..1].iter()), "one");
249 assert_writeable_eq!(formatter.format(values[0..2].iter()), "$one;two+");
250 assert_writeable_eq!(formatter.format(values[0..3].iter()), "@one:two.three!");
251 assert_writeable_eq!(
252 formatter.format(values[0..4].iter()),
253 "@one:two,three.four!"
254 );
255
256 assert_writeable_parts_eq!(
257 formatter.format(values.iter()),
258 "@one:two,three,four.five!",
259 [
260 (0, 1, parts::LITERAL),
261 (1, 4, parts::ELEMENT),
262 (4, 5, parts::LITERAL),
263 (5, 8, parts::ELEMENT),
264 (8, 9, parts::LITERAL),
265 (9, 14, parts::ELEMENT),
266 (14, 15, parts::LITERAL),
267 (15, 19, parts::ELEMENT),
268 (19, 20, parts::LITERAL),
269 (20, 24, parts::ELEMENT),
270 (24, 25, parts::LITERAL)
271 ]
272 );
273 }
274
275 #[test]
276 fn test_into_iterator() {
277 let formatter = formatter(ListLength::Wide);
278
279 let mut vecdeque = std::collections::vec_deque::VecDeque::<u8>::new();
280 vecdeque.push_back(10);
281 vecdeque.push_front(48);
282
283 assert_writeable_parts_eq!(
284 formatter.format(vecdeque.iter()),
285 "$48;10+",
286 [
287 (0, 1, parts::LITERAL),
288 (1, 3, parts::ELEMENT),
289 (3, 4, parts::LITERAL),
290 (4, 6, parts::ELEMENT),
291 (6, 7, parts::LITERAL),
292 ]
293 );
294 }
295
296 #[test]
297 fn test_iterator() {
298 let formatter = formatter(ListLength::Wide);
299
300 assert_writeable_parts_eq!(
301 formatter.format(core::iter::repeat(5).take(2)),
302 "$5;5+",
303 [
304 (0, 1, parts::LITERAL),
305 (1, 2, parts::ELEMENT),
306 (2, 3, parts::LITERAL),
307 (3, 4, parts::ELEMENT),
308 (4, 5, parts::LITERAL),
309 ]
310 );
311 }
312
313 #[test]
314 fn test_conditional() {
315 let formatter = formatter(ListLength::Narrow);
316
317 assert_writeable_eq!(formatter.format(["Beta", "Alpha"].iter()), "Beta :o Alpha");
318 }
319 }