1#[cfg(feature = "databake")]
6mod databake;
7#[cfg(feature = "serde")]
8pub(crate) mod serde;
9#[cfg(feature = "zerovec")]
10mod zerovec;
11
12use crate::common::*;
13#[cfg(feature = "alloc")]
14use crate::Error;
15#[cfg(feature = "alloc")]
16use crate::Parser;
17#[cfg(feature = "alloc")]
18use crate::ParserOptions;
19#[cfg(feature = "alloc")]
20use alloc::{borrow::ToOwned, boxed::Box, str::FromStr, string::String};
21use core::{convert::Infallible, fmt, marker::PhantomData};
22use writeable::{adapters::TryWriteableInfallibleAsWriteable, PartsWrite, TryWriteable, Writeable};
23
24#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
67#[repr(transparent)]
68pub struct Pattern<B: PatternBackend> {
69 _backend: PhantomData<B>,
70 pub store: B::Store,
72}
73
74impl<B: PatternBackend> PartialEq for Pattern<B> {
75 fn eq(&self, other: &Self) -> bool {
76 self.store == other.store
77 }
78}
79
80impl<B: PatternBackend> core::fmt::Debug for Pattern<B> {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 f.debug_struct("Pattern")
83 .field("_backend", &self._backend)
84 .field("store", &&self.store)
85 .finish()
86 }
87}
88
89impl<B: PatternBackend> Default for &'static Pattern<B> {
90 fn default() -> Self {
91 Pattern::from_ref_store_unchecked(B::empty())
92 }
93}
94
95#[cfg(feature = "alloc")]
96impl<B: PatternBackend> Default for Box<Pattern<B>>
97where
98 Box<B::Store>: From<&'static B::Store>,
99{
100 fn default() -> Self {
101 Pattern::from_boxed_store_unchecked(Box::from(B::empty()))
102 }
103}
104
105#[test]
106fn test_defaults() {
107 assert_eq!(
108 Box::<Pattern::<crate::SinglePlaceholder>>::default(),
109 Pattern::try_from_items(core::iter::empty()).unwrap()
110 );
111 assert_eq!(
112 Box::<Pattern::<crate::DoublePlaceholder>>::default(),
113 Pattern::try_from_items(core::iter::empty()).unwrap()
114 );
115 assert_eq!(
116 Box::<Pattern::<crate::MultiNamedPlaceholder>>::default(),
117 Pattern::try_from_items(core::iter::empty()).unwrap()
118 );
119}
120
121#[cfg(feature = "alloc")]
122impl<B: PatternBackend> ToOwned for Pattern<B>
123where
124 Box<B::Store>: for<'a> From<&'a B::Store>,
125{
126 type Owned = Box<Pattern<B>>;
127
128 fn to_owned(&self) -> Self::Owned {
129 Self::from_boxed_store_unchecked(Box::from(&self.store))
130 }
131}
132
133#[cfg(feature = "alloc")]
134impl<B: PatternBackend> Clone for Box<Pattern<B>>
135where
136 Box<B::Store>: for<'a> From<&'a B::Store>,
137{
138 fn clone(&self) -> Self {
139 Pattern::from_boxed_store_unchecked(Box::from(&self.store))
140 }
141}
142
143impl<B: PatternBackend> Pattern<B> {
144 #[cfg(feature = "alloc")]
145 pub(crate) const fn from_boxed_store_unchecked(store: Box<B::Store>) -> Box<Self> {
146 unsafe { core::mem::transmute(store) }
148 }
149
150 #[doc(hidden)] pub const fn from_ref_store_unchecked(store: &B::Store) -> &Self {
152 unsafe { &*(store as *const B::Store as *const Self) }
154 }
155}
156
157#[cfg(feature = "alloc")]
158impl<B> Pattern<B>
159where
160 B: PatternBackend,
161{
162 pub fn try_from_items<'a, I>(items: I) -> Result<Box<Self>, Error>
185 where
186 I: Iterator<Item = PatternItemCow<'a, B::PlaceholderKeyCow<'a>>>,
187 {
188 let store = B::try_from_items(items.map(Ok))?;
189 #[cfg(debug_assertions)]
190 match B::validate_store(core::borrow::Borrow::borrow(&store)) {
191 Ok(()) => (),
192 Err(e) => {
193 debug_assert!(false, "{e:?}");
194 }
195 };
196 Ok(Self::from_boxed_store_unchecked(store))
197 }
198}
199
200#[cfg(feature = "alloc")]
201impl<'a, B> Pattern<B>
202where
203 B: PatternBackend,
204 B::PlaceholderKeyCow<'a>: FromStr,
205 <B::PlaceholderKeyCow<'a> as FromStr>::Err: fmt::Debug,
206{
207 pub fn try_from_str(pattern: &str, options: ParserOptions) -> Result<Box<Self>, Error> {
226 let parser = Parser::new(pattern, options);
227 let store = B::try_from_items(parser)?;
228 #[cfg(debug_assertions)]
229 match B::validate_store(core::borrow::Borrow::borrow(&store)) {
230 Ok(()) => (),
231 Err(e) => {
232 debug_assert!(false, "{e:?} for pattern {pattern:?}");
233 }
234 };
235 Ok(Self::from_boxed_store_unchecked(store))
236 }
237}
238
239impl<B> Pattern<B>
240where
241 B: PatternBackend,
242{
243 pub fn iter(&self) -> impl Iterator<Item = PatternItem<'_, B::PlaceholderKey<'_>>> + '_ {
245 B::iter_items(&self.store)
246 }
247
248 pub fn try_interpolate<'a, P>(
251 &'a self,
252 value_provider: P,
253 ) -> impl TryWriteable<Error = B::Error<'a>> + fmt::Display + 'a
254 where
255 P: PlaceholderValueProvider<B::PlaceholderKey<'a>, Error = B::Error<'a>> + 'a,
256 {
257 WriteablePattern::<B, P> {
258 store: &self.store,
259 value_provider,
260 }
261 }
262
263 #[cfg(feature = "alloc")]
264 pub fn try_interpolate_to_string<'a, P>(
270 &'a self,
271 value_provider: P,
272 ) -> Result<String, (B::Error<'a>, String)>
273 where
274 P: PlaceholderValueProvider<B::PlaceholderKey<'a>, Error = B::Error<'a>> + 'a,
275 {
276 self.try_interpolate(value_provider)
277 .try_write_to_string()
278 .map(|s| s.into_owned())
279 .map_err(|(e, s)| (e, s.into_owned()))
280 }
281}
282
283impl<B> Pattern<B>
284where
285 for<'b> B: PatternBackend<Error<'b> = Infallible>,
286{
287 pub fn interpolate<'a, P>(&'a self, value_provider: P) -> impl Writeable + fmt::Display + 'a
290 where
291 P: PlaceholderValueProvider<B::PlaceholderKey<'a>, Error = B::Error<'a>> + 'a,
292 {
293 TryWriteableInfallibleAsWriteable(WriteablePattern::<B, P> {
294 store: &self.store,
295 value_provider,
296 })
297 }
298
299 #[cfg(feature = "alloc")]
300 pub fn interpolate_to_string<'a, P>(&'a self, value_provider: P) -> String
304 where
305 P: PlaceholderValueProvider<B::PlaceholderKey<'a>, Error = B::Error<'a>> + 'a,
306 {
307 self.interpolate(value_provider)
308 .write_to_string()
309 .into_owned()
310 }
311}
312
313struct WriteablePattern<'a, B: PatternBackend, P> {
314 store: &'a B::Store,
315 value_provider: P,
316}
317
318impl<'a, B, P> TryWriteable for WriteablePattern<'a, B, P>
319where
320 B: PatternBackend,
321 P: PlaceholderValueProvider<B::PlaceholderKey<'a>, Error = B::Error<'a>>,
322{
323 type Error = B::Error<'a>;
324
325 fn try_write_to_parts<S: PartsWrite + ?Sized>(
326 &self,
327 sink: &mut S,
328 ) -> Result<Result<(), Self::Error>, fmt::Error> {
329 let mut error = None;
330 let it = B::iter_items(self.store);
331 #[cfg(debug_assertions)]
332 let (size_hint, mut actual_len) = (it.size_hint(), 0);
333 for item in it {
334 match item {
335 PatternItem::Literal(s) => {
336 self.value_provider.map_literal(s).write_to_parts(sink)?;
337 }
338 PatternItem::Placeholder(key) => {
339 let element_writeable = self.value_provider.value_for(key);
340 if let Err(e) = element_writeable.try_write_to_parts(sink)? {
341 error.get_or_insert(e);
343 }
344 }
345 }
346 #[cfg(debug_assertions)]
347 {
348 actual_len += 1;
349 }
350 }
351 #[cfg(debug_assertions)]
352 {
353 debug_assert!(actual_len >= size_hint.0);
354 if let Some(max_len) = size_hint.1 {
355 debug_assert!(actual_len <= max_len);
356 }
357 }
358 if let Some(e) = error {
359 Ok(Err(e))
360 } else {
361 Ok(Ok(()))
362 }
363 }
364}
365
366impl<'a, B, P> fmt::Display for WriteablePattern<'a, B, P>
367where
368 B: PatternBackend,
369 P: PlaceholderValueProvider<B::PlaceholderKey<'a>, Error = B::Error<'a>>,
370{
371 #[inline]
372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373 self.try_write_to(f).map(|_| ())
375 }
376}
377
378#[test]
379fn test_try_from_str_inference() {
380 use crate::SinglePlaceholder;
381 let _: Box<Pattern<SinglePlaceholder>> =
382 Pattern::try_from_str("{0} days", Default::default()).unwrap();
383 let _ = Pattern::<SinglePlaceholder>::try_from_str("{0} days", Default::default()).unwrap();
384}