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}