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 ).
5 use crate::provider
::{AndListV1Marker, ErasedListV1Marker, OrListV1Marker, UnitListV1Marker}
;
8 use core
::fmt
::{self, Write}
;
9 use icu_provider
::prelude
::*;
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
>,
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
>(
29 ) -> Result
<Self, ListError
> {
30 let data
= data_provider
33 metadata
: Default
::default(),
35 .take_payload()?
.cast();
36 Ok(Self { data, length }
)
38 icu_provider
::gen_any_buffer_constructors
!(
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
,
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
,
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
,
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
>(
80 ) -> FormattedList
<'a
, W
, I
> {
87 /// Returns a [`String`] composed of the input [`Writeable`]s and the language-dependent
89 pub fn format_to_string
<W
: Writeable
, I
: Iterator
<Item
= W
> + Clone
>(
92 ) -> alloc
::string
::String
{
93 self.format(values
).write_to_string().into_owned()
97 /// The [`Part`]s used by [`ListFormatter`].
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
{
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
{
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
,
122 impl<'a
, W
: Writeable
+ 'a
, I
: Iterator
<Item
= W
> + Clone
+ 'a
> Writeable
123 for FormattedList
<'a
, W
, I
>
125 fn write_to_parts
<V
: PartsWrite
+ ?Sized
>(&self, sink
: &mut V
) -> fmt
::Result
{
126 macro_rules
! literal
{
128 sink
.with_part(parts
::LITERAL
, |l
| l
.write_str($lit
))
133 sink
.with_part(parts
::ELEMENT
, |e
| $val
.write_to_parts(e
))
137 let mut values
= self.values
.clone();
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
146 let (start_before
, start_between
, _
) = self
150 .start(self.formatter
.length
)
153 literal
!(start_before
)?
;
155 literal
!(start_between
)?
;
158 let mut next
= third
;
160 for next_next
in values
{
161 let (_
, between
, _
) = self
165 .middle(self.formatter
.length
)
172 let (_
, end_between
, end_after
) = self
176 .end(self.formatter
.length
)
178 literal
!(end_between
)?
;
182 // Pair(values[0], values[1]) = pair_before + values[0] + pair_between + values[1] + pair_after
183 let (before
, between
, after
) = self
187 .pair(self.formatter
.length
)
203 fn writeable_length_hint(&self) -> LengthHint
{
205 let item_length
= self
210 w
.writeable_length_hint()
212 .sum
::<LengthHint
>();
218 .size_hint(self.formatter
.length
, count
)
222 impl<'a
, W
: Writeable
+ 'a
, I
: Iterator
<Item
= W
> + Clone
+ 'a
> core
::fmt
::Display
223 for FormattedList
<'a
, W
, I
>
225 fn fmt(&self, f
: &mut core
::fmt
::Formatter
<'_
>) -> core
::fmt
::Result
{
230 #[cfg(all(test, feature = "datagen"))]
233 use writeable
::{assert_writeable_eq, assert_writeable_parts_eq}
;
235 fn formatter(length
: ListLength
) -> ListFormatter
{
237 data
: DataPayload
::from_owned(crate::provider
::test
::test_patterns()),
244 let formatter
= formatter(ListLength
::Wide
);
245 let values
= ["one", "two", "three", "four", "five"];
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!"
256 assert_writeable_parts_eq
!(
257 formatter
.format(values
.iter()),
258 "@one:two,three,four.five!",
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
)
276 fn test_into_iterator() {
277 let formatter
= formatter(ListLength
::Wide
);
279 let mut vecdeque
= std
::collections
::vec_deque
::VecDeque
::<u8>::new();
280 vecdeque
.push_back(10);
281 vecdeque
.push_front(48);
283 assert_writeable_parts_eq
!(
284 formatter
.format(vecdeque
.iter()),
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
),
298 let formatter
= formatter(ListLength
::Wide
);
300 assert_writeable_parts_eq
!(
301 formatter
.format(core
::iter
::repeat(5).take(2)),
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
),
314 fn test_conditional() {
315 let formatter
= formatter(ListLength
::Narrow
);
317 assert_writeable_eq
!(formatter
.format(["Beta", "Alpha"].iter()), "Beta :o Alpha");