icu_pattern/
common.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::Error;
6use writeable::{TryWriteable, Writeable};
7
8#[cfg(feature = "alloc")]
9use alloc::{borrow::Cow, boxed::Box};
10
11/// A borrowed item in a [`Pattern`]. Items are either string literals or placeholders.
12///
13/// [`Pattern`]: crate::Pattern
14#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
15#[allow(clippy::exhaustive_enums)] // Part of core data model
16pub enum PatternItem<'a, T> {
17    /// A placeholder of the type specified on this [`PatternItem`].
18    Placeholder(T),
19    /// A string literal. This can occur in one of three places:
20    ///
21    /// 1. Between the start of the string and the first placeholder (prefix)
22    /// 2. Between two placeholders (infix)
23    /// 3. Between the final placeholder and the end of the string (suffix)
24    Literal(&'a str),
25}
26
27/// A borrowed-or-owned item in a [`Pattern`]. Items are either string literals or placeholders.
28///
29/// ✨ *Enabled with the `alloc` Cargo feature.*
30///
31/// [`Pattern`]: crate::Pattern
32#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
33#[allow(clippy::exhaustive_enums)] // Part of core data model
34#[cfg(feature = "alloc")]
35#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
36pub enum PatternItemCow<'a, T> {
37    /// A placeholder of the type specified on this [`PatternItemCow`].
38    Placeholder(T),
39    /// A string literal. This can occur in one of three places:
40    ///
41    /// 1. Between the start of the string and the first placeholder (prefix)
42    /// 2. Between two placeholders (infix)
43    /// 3. Between the final placeholder and the end of the string (suffix)
44    #[cfg_attr(feature = "serde", serde(borrow))]
45    Literal(Cow<'a, str>),
46}
47
48#[cfg(feature = "alloc")]
49impl<'a, T, U> From<PatternItem<'a, U>> for PatternItemCow<'a, T>
50where
51    T: From<U>,
52{
53    fn from(value: PatternItem<'a, U>) -> Self {
54        match value {
55            PatternItem::Placeholder(t) => Self::Placeholder(t.into()),
56            PatternItem::Literal(s) => Self::Literal(Cow::Borrowed(s)),
57        }
58    }
59}
60
61/// Types that implement backing data models for [`Pattern`] implement this trait.
62///
63/// The trait has no public methods and is not implementable outside of this crate.
64///
65/// <div class="stab unstable">
66/// 🚫 This trait is sealed; it cannot be implemented by user code. If an API requests an item that implements this
67/// trait, please consider using a type from the implementors listed below.
68/// </div>
69///
70/// [`Pattern`]: crate::Pattern
71// Debug so that `#[derive(Debug)]` on types generic in `PatternBackend` works
72pub trait PatternBackend: crate::private::Sealed + 'static + core::fmt::Debug {
73    /// The type to be used as the placeholder key in code.
74    type PlaceholderKey<'a>;
75
76    /// Cowable version of the type to be used as the placeholder key in code.
77    // Note: it is not good practice to feature-gate trait methods, but this trait is sealed
78    #[cfg(feature = "alloc")]
79    type PlaceholderKeyCow<'a>;
80
81    /// The type of error that the [`TryWriteable`] for this backend can return.
82    type Error<'a>;
83
84    /// The unsized type of the store required for this backend, usually `str` or `[u8]`.
85    #[doc(hidden)] // TODO(#4467): Should be internal
86    type Store: ?Sized + PartialEq + core::fmt::Debug;
87
88    /// The iterator type returned by [`Self::try_from_items`].
89    #[doc(hidden)] // TODO(#4467): Should be internal
90    type Iter<'a>: Iterator<Item = PatternItem<'a, Self::PlaceholderKey<'a>>>;
91
92    /// Checks a store for validity, returning an error if invalid.
93    #[doc(hidden)] // TODO(#4467): Should be internal
94    fn validate_store(store: &Self::Store) -> Result<(), Error>;
95
96    /// Constructs a store from pattern items.
97    #[doc(hidden)]
98    // TODO(#4467): Should be internal
99    // Note: it is not good practice to feature-gate trait methods, but this trait is sealed
100    #[cfg(feature = "alloc")]
101    fn try_from_items<
102        'cow,
103        'ph,
104        I: Iterator<Item = Result<PatternItemCow<'cow, Self::PlaceholderKeyCow<'ph>>, Error>>,
105    >(
106        items: I,
107    ) -> Result<Box<Self::Store>, Error>;
108
109    /// Iterates over the pattern items in a store.
110    #[doc(hidden)] // TODO(#4467): Should be internal
111    fn iter_items(store: &Self::Store) -> Self::Iter<'_>;
112
113    /// The store for the empty pattern, used to implement `Default`
114    #[doc(hidden)] // TODO(#4467): Should be internal
115    fn empty() -> &'static Self::Store;
116}
117
118/// Trait implemented on collections that can produce [`TryWriteable`]s for interpolation.
119///
120/// This trait can add [`Part`]s for individual literals or placeholders. The implementations
121/// of this trait on standard types do not add any [`Part`]s.
122///
123/// This trait has a blanket implementation and is therefore not implementable by user code.
124///
125/// # Examples
126///
127/// A custom implementation that adds parts:
128///
129/// ```
130/// use core::str::FromStr;
131/// use icu_pattern::Pattern;
132/// use icu_pattern::DoublePlaceholder;
133/// use icu_pattern::DoublePlaceholderKey;
134/// use icu_pattern::PlaceholderValueProvider;
135/// use writeable::adapters::WithPart;
136/// use writeable::adapters::WriteableAsTryWriteableInfallible;
137/// use writeable::assert_writeable_parts_eq;
138/// use writeable::Part;
139/// use writeable::Writeable;
140///
141/// let pattern = Pattern::<DoublePlaceholder>::try_from_str(
142///     "Hello, {0} and {1}!",
143///     Default::default(),
144/// )
145/// .unwrap();
146///
147/// struct ValuesWithParts<'a>(&'a str, &'a str);
148///
149/// const PART_PLACEHOLDER_0: Part = Part {
150///     category: "custom",
151///     value: "placeholder0",
152/// };
153/// const PART_PLACEHOLDER_1: Part = Part {
154///     category: "custom",
155///     value: "placeholder1",
156/// };
157/// const PART_LITERAL: Part = Part {
158///     category: "custom",
159///     value: "literal",
160/// };
161///
162/// impl PlaceholderValueProvider<DoublePlaceholderKey> for ValuesWithParts<'_> {
163///     type Error = core::convert::Infallible;
164///
165///     type W<'a> = WriteableAsTryWriteableInfallible<WithPart<&'a str>>
166///     where
167///         Self: 'a;
168///
169///     type L<'a, 'l> = WithPart<&'l str>
170///     where
171///         Self: 'a;
172///
173///     #[inline]
174///     fn value_for(&self, key: DoublePlaceholderKey) -> Self::W<'_> {
175///         let writeable = match key {
176///             DoublePlaceholderKey::Place0 => WithPart {
177///                 writeable: self.0,
178///                 part: PART_PLACEHOLDER_0,
179///             },
180///             DoublePlaceholderKey::Place1 => WithPart {
181///                 writeable: self.1,
182///                 part: PART_PLACEHOLDER_1,
183///             },
184///         };
185///         WriteableAsTryWriteableInfallible(writeable)
186///     }
187///
188///     #[inline]
189///     fn map_literal<'a, 'l>(&'a self, literal: &'l str) -> Self::L<'a, 'l> {
190///         WithPart {
191///             writeable: literal,
192///             part: PART_LITERAL,
193///         }
194///     }
195/// }
196///
197/// assert_writeable_parts_eq!(
198///     pattern.interpolate(ValuesWithParts("Alice", "Bob")),
199///     "Hello, Alice and Bob!",
200///     [
201///         (0, 7, PART_LITERAL),
202///         (7, 12, PART_PLACEHOLDER_0),
203///         (12, 17, PART_LITERAL),
204///         (17, 20, PART_PLACEHOLDER_1),
205///         (20, 21, PART_LITERAL),
206///     ]
207/// );
208/// ```
209///
210/// [`Part`]: writeable::Part
211pub trait PlaceholderValueProvider<K> {
212    type Error;
213
214    /// The type of [`TryWriteable`] returned by [`Self::value_for`].
215    ///
216    /// To return a [`Writeable`], wrap it with [`WriteableAsTryWriteableInfallible`].
217    ///
218    /// [`WriteableAsTryWriteableInfallible`]: writeable::adapters::WriteableAsTryWriteableInfallible
219    type W<'a>: TryWriteable<Error = Self::Error>
220    where
221        Self: 'a;
222
223    /// The type of [`Writeable`] returned by [`Self::map_literal`].
224    ///
225    /// If you are not adding parts, this can be `&'l str`.
226    type L<'a, 'l>: Writeable
227    where
228        Self: 'a;
229
230    /// Returns the [`TryWriteable`] to substitute for the given placeholder.
231    ///
232    /// See [`PatternItem::Placeholder`]
233    fn value_for(&self, key: K) -> Self::W<'_>;
234
235    /// Maps a literal string to a [`Writeable`] that could contain parts.
236    ///
237    /// See [`PatternItem::Literal`]
238    fn map_literal<'a, 'l>(&'a self, literal: &'l str) -> Self::L<'a, 'l>;
239}
240
241impl<K, T> PlaceholderValueProvider<K> for &'_ T
242where
243    T: PlaceholderValueProvider<K> + ?Sized,
244{
245    type Error = T::Error;
246
247    type W<'a>
248        = T::W<'a>
249    where
250        Self: 'a;
251
252    type L<'a, 'l>
253        = T::L<'a, 'l>
254    where
255        Self: 'a;
256
257    fn value_for(&self, key: K) -> Self::W<'_> {
258        (*self).value_for(key)
259    }
260    fn map_literal<'a, 'l>(&'a self, literal: &'l str) -> Self::L<'a, 'l> {
261        (*self).map_literal(literal)
262    }
263}