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