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}