From 4547b622d8d29df964fa2914213088b148c498fc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:18:32 +0200 Subject: Merging upstream version 1.67.1+dfsg1. Signed-off-by: Daniel Baumann --- vendor/icu_list/src/list_formatter.rs | 319 ++++++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 vendor/icu_list/src/list_formatter.rs (limited to 'vendor/icu_list/src/list_formatter.rs') diff --git a/vendor/icu_list/src/list_formatter.rs b/vendor/icu_list/src/list_formatter.rs new file mode 100644 index 000000000..36f5fbb7b --- /dev/null +++ b/vendor/icu_list/src/list_formatter.rs @@ -0,0 +1,319 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +use crate::provider::{AndListV1Marker, ErasedListV1Marker, OrListV1Marker, UnitListV1Marker}; +use crate::ListError; +use crate::ListLength; +use core::fmt::{self, Write}; +use icu_provider::prelude::*; +use writeable::*; + +/// A formatter that renders sequences of items in an i18n-friendly way. See the +/// [crate-level documentation](crate) for more details. +pub struct ListFormatter { + data: DataPayload, + length: ListLength, +} + +macro_rules! constructor { + ($name: ident, $name_any: ident, $name_buffer: ident, $marker: ty, $doc: literal) => { + #[doc = concat!("Creates a new [`ListFormatter`] that produces a ", $doc, "-type list.\n\nSee the [CLDR spec]", + "(https://unicode.org/reports/tr35/tr35-general.html#ListPatterns) for an explanation of the different types.\n\n", + "[📚 Help choosing a constructor](icu_provider::constructors)\n\n", + "
⚠️ The bounds on this function may change over time, including in SemVer minor releases.
")] + pub fn $name + ?Sized>( + data_provider: &D, + locale: &DataLocale, + length: ListLength, + ) -> Result { + let data = data_provider + .load(DataRequest { + locale, + metadata: Default::default(), + })? + .take_payload()?.cast(); + Ok(Self { data, length }) + } + icu_provider::gen_any_buffer_constructors!( + locale: include, + style: ListLength, + error: ListError, + functions: [ + Self::$name, + $name_any, + $name_buffer + ] + ); + }; +} + +impl ListFormatter { + constructor!( + try_new_and_with_length_unstable, + try_new_and_with_length_with_any_provider, + try_new_and_with_length_with_buffer_provider, + AndListV1Marker, + "and" + ); + constructor!( + try_new_or_with_length_unstable, + try_new_or_with_length_with_any_provider, + try_new_or_with_length_with_buffer_provider, + OrListV1Marker, + "or" + ); + constructor!( + try_new_unit_with_length_unstable, + try_new_unit_with_length_with_any_provider, + try_new_unit_with_length_with_buffer_provider, + UnitListV1Marker, + "unit" + ); + + /// Returns a [`Writeable`] composed of the input [`Writeable`]s and the language-dependent + /// formatting. The first layer of parts contains [`parts::ELEMENT`] for input + /// elements, and [`parts::LITERAL`] for list literals. + pub fn format<'a, W: Writeable + 'a, I: Iterator + Clone + 'a>( + &'a self, + values: I, + ) -> FormattedList<'a, W, I> { + FormattedList { + formatter: self, + values, + } + } + + /// Returns a [`String`] composed of the input [`Writeable`]s and the language-dependent + /// formatting. + pub fn format_to_string + Clone>( + &self, + values: I, + ) -> alloc::string::String { + self.format(values).write_to_string().into_owned() + } +} + +/// The [`Part`]s used by [`ListFormatter`]. +pub mod parts { + use writeable::Part; + + /// The [`Part`] used by [`FormattedList`](super::FormattedList) to mark the part of the string that is an element. + pub const ELEMENT: Part = Part { + category: "list", + value: "element", + }; + + /// The [`Part`] used by [`FormattedList`](super::FormattedList) to mark the part of the string that is a list literal, + /// such as ", " or " and ". + pub const LITERAL: Part = Part { + category: "list", + value: "literal", + }; +} + +/// The [`Writeable`] implementation that is returned by [`ListFormatter::format`]. See +/// the [`writeable`] crate for how to consume this. +pub struct FormattedList<'a, W: Writeable + 'a, I: Iterator + Clone + 'a> { + formatter: &'a ListFormatter, + values: I, +} + +impl<'a, W: Writeable + 'a, I: Iterator + Clone + 'a> Writeable + for FormattedList<'a, W, I> +{ + fn write_to_parts(&self, sink: &mut V) -> fmt::Result { + macro_rules! literal { + ($lit:ident) => { + sink.with_part(parts::LITERAL, |l| l.write_str($lit)) + }; + } + macro_rules! value { + ($val:expr) => { + sink.with_part(parts::ELEMENT, |e| $val.write_to_parts(e)) + }; + } + + let mut values = self.values.clone(); + + if let Some(first) = values.next() { + if let Some(second) = values.next() { + if let Some(third) = values.next() { + // Start(values[0], middle(..., middle(values[n-3], End(values[n-2], values[n-1]))...)) = + // start_before + values[0] + start_between + (values[1..n-3] + middle_between)* + + // values[n-2] + end_between + values[n-1] + end_after + + let (start_before, start_between, _) = self + .formatter + .data + .get() + .start(self.formatter.length) + .parts(&second); + + literal!(start_before)?; + value!(first)?; + literal!(start_between)?; + value!(second)?; + + let mut next = third; + + for next_next in values { + let (_, between, _) = self + .formatter + .data + .get() + .middle(self.formatter.length) + .parts(&next); + literal!(between)?; + value!(next)?; + next = next_next; + } + + let (_, end_between, end_after) = self + .formatter + .data + .get() + .end(self.formatter.length) + .parts(&next); + literal!(end_between)?; + value!(next)?; + literal!(end_after) + } else { + // Pair(values[0], values[1]) = pair_before + values[0] + pair_between + values[1] + pair_after + let (before, between, after) = self + .formatter + .data + .get() + .pair(self.formatter.length) + .parts(&second); + literal!(before)?; + value!(first)?; + literal!(between)?; + value!(second)?; + literal!(after) + } + } else { + value!(first) + } + } else { + Ok(()) + } + } + + fn writeable_length_hint(&self) -> LengthHint { + let mut count = 0; + let item_length = self + .values + .clone() + .map(|w| { + count += 1; + w.writeable_length_hint() + }) + .sum::(); + item_length + + self + .formatter + .data + .get() + .size_hint(self.formatter.length, count) + } +} + +impl<'a, W: Writeable + 'a, I: Iterator + Clone + 'a> core::fmt::Display + for FormattedList<'a, W, I> +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.write_to(f) + } +} + +#[cfg(all(test, feature = "datagen"))] +mod tests { + use super::*; + use writeable::{assert_writeable_eq, assert_writeable_parts_eq}; + + fn formatter(length: ListLength) -> ListFormatter { + ListFormatter { + data: DataPayload::from_owned(crate::provider::test::test_patterns()), + length, + } + } + + #[test] + fn test_slices() { + let formatter = formatter(ListLength::Wide); + let values = ["one", "two", "three", "four", "five"]; + + assert_writeable_eq!(formatter.format(values[0..0].iter()), ""); + assert_writeable_eq!(formatter.format(values[0..1].iter()), "one"); + assert_writeable_eq!(formatter.format(values[0..2].iter()), "$one;two+"); + assert_writeable_eq!(formatter.format(values[0..3].iter()), "@one:two.three!"); + assert_writeable_eq!( + formatter.format(values[0..4].iter()), + "@one:two,three.four!" + ); + + assert_writeable_parts_eq!( + formatter.format(values.iter()), + "@one:two,three,four.five!", + [ + (0, 1, parts::LITERAL), + (1, 4, parts::ELEMENT), + (4, 5, parts::LITERAL), + (5, 8, parts::ELEMENT), + (8, 9, parts::LITERAL), + (9, 14, parts::ELEMENT), + (14, 15, parts::LITERAL), + (15, 19, parts::ELEMENT), + (19, 20, parts::LITERAL), + (20, 24, parts::ELEMENT), + (24, 25, parts::LITERAL) + ] + ); + } + + #[test] + fn test_into_iterator() { + let formatter = formatter(ListLength::Wide); + + let mut vecdeque = std::collections::vec_deque::VecDeque::::new(); + vecdeque.push_back(10); + vecdeque.push_front(48); + + assert_writeable_parts_eq!( + formatter.format(vecdeque.iter()), + "$48;10+", + [ + (0, 1, parts::LITERAL), + (1, 3, parts::ELEMENT), + (3, 4, parts::LITERAL), + (4, 6, parts::ELEMENT), + (6, 7, parts::LITERAL), + ] + ); + } + + #[test] + fn test_iterator() { + let formatter = formatter(ListLength::Wide); + + assert_writeable_parts_eq!( + formatter.format(core::iter::repeat(5).take(2)), + "$5;5+", + [ + (0, 1, parts::LITERAL), + (1, 2, parts::ELEMENT), + (2, 3, parts::LITERAL), + (3, 4, parts::ELEMENT), + (4, 5, parts::LITERAL), + ] + ); + } + + #[test] + fn test_conditional() { + let formatter = formatter(ListLength::Narrow); + + assert_writeable_eq!(formatter.format(["Beta", "Alpha"].iter()), "Beta :o Alpha"); + } +} -- cgit v1.2.3