Skip to content

Commit 21efbae

Browse files
authored
Implement try_from_bytes for Pattern (#5034)
1 parent 63fa8da commit 21efbae

File tree

8 files changed

+74
-12
lines changed

8 files changed

+74
-12
lines changed

utils/pattern/src/common.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,21 @@ pub trait PatternBackend: crate::private::Sealed + 'static {
7575
/// The type of error that the [`TryWriteable`] for this backend can return.
7676
type Error<'a>;
7777

78+
/// The type of error that the [`PatternBackend::try_store_from_bytes`] can return.
79+
type StoreFromBytesError;
80+
7881
/// The unsized type of the store required for this backend, usually `str` or `[u8]`.
7982
type Store: ?Sized;
8083

8184
/// The iterator type returned by [`Self::try_from_items`].
8285
#[doc(hidden)] // TODO(#4467): Should be internal
8386
type Iter<'a>: Iterator<Item = PatternItem<'a, Self::PlaceholderKey<'a>>>;
8487

88+
/// Converts a byte slice store to this pattern backend's store.
89+
/// Does not perform validation of the store.
90+
#[doc(hidden)] // TODO(#4467): Should be internal
91+
fn try_store_from_bytes(bytes: &[u8]) -> Result<&Self::Store, Self::StoreFromBytesError>;
92+
8593
/// Checks a store for validity, returning an error if invalid.
8694
#[doc(hidden)] // TODO(#4467): Should be internal
8795
fn validate_store(store: &Self::Store) -> Result<(), Error>;

utils/pattern/src/double.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//! Code for the [`DoublePlaceholder`] pattern backend.
66
77
use core::convert::Infallible;
8+
use core::str::Utf8Error;
89
use core::{cmp::Ordering, str::FromStr};
910
use either::Either;
1011
use writeable::adapters::WriteableAsTryWriteableInfallible;
@@ -270,9 +271,15 @@ impl PatternBackend for DoublePlaceholder {
270271
#[cfg(feature = "alloc")]
271272
type PlaceholderKeyCow<'a> = DoublePlaceholderKey;
272273
type Error<'a> = Infallible;
274+
type StoreFromBytesError = Utf8Error;
273275
type Store = str;
274276
type Iter<'a> = DoublePlaceholderPatternIterator<'a>;
275277

278+
#[inline]
279+
fn try_store_from_bytes(bytes: &[u8]) -> Result<&Self::Store, Self::StoreFromBytesError> {
280+
core::str::from_utf8(bytes)
281+
}
282+
276283
fn validate_store(store: &Self::Store) -> Result<(), Error> {
277284
let mut chars = store.chars();
278285
let ph_first_char = chars.next().ok_or(Error::InvalidPattern)?;

utils/pattern/src/error.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,13 @@ pub enum PatternError {
1313
InvalidPlaceholder,
1414
}
1515

16+
/// Error type for [`crate::Pattern::try_from_bytes_store`].
17+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
18+
#[non_exhaustive]
19+
pub enum PatternOrUtf8Error<E> {
20+
Pattern(PatternError),
21+
Utf8(E),
22+
}
23+
1624
#[cfg(feature = "std")]
1725
impl std::error::Error for PatternError {}

utils/pattern/src/frontend/mod.rs

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,19 @@
66
mod databake;
77
#[cfg(feature = "serde")]
88
mod serde;
9-
10-
use core::{
11-
convert::Infallible,
12-
fmt::{self, Write},
13-
marker::PhantomData,
14-
};
15-
16-
use writeable::{adapters::TryWriteableInfallibleAsWriteable, PartsWrite, TryWriteable, Writeable};
17-
189
use crate::common::*;
1910
use crate::Error;
20-
11+
use crate::PatternOrUtf8Error;
2112
#[cfg(feature = "alloc")]
2213
use crate::{Parser, ParserOptions};
2314
#[cfg(feature = "alloc")]
2415
use alloc::{borrow::ToOwned, str::FromStr, string::String};
16+
use core::{
17+
convert::Infallible,
18+
fmt::{self, Write},
19+
marker::PhantomData,
20+
};
21+
use writeable::{adapters::TryWriteableInfallibleAsWriteable, PartsWrite, TryWriteable, Writeable};
2522

2623
/// A string pattern with placeholders.
2724
///
@@ -167,6 +164,33 @@ where
167164
}
168165
}
169166

167+
impl<'a, B> Pattern<B, &'a B::Store>
168+
where
169+
B: PatternBackend,
170+
{
171+
/// Creates a pattern from its store encoded as bytes.
172+
///
173+
/// # Examples
174+
///
175+
/// ```
176+
/// use icu_pattern::Pattern;
177+
/// use icu_pattern::SinglePlaceholder;
178+
///
179+
/// Pattern::<SinglePlaceholder, _>::try_from_bytes_store(b"\x01 days")
180+
/// .expect("single placeholder pattern");
181+
/// ```
182+
pub fn try_from_bytes_store(
183+
bytes: &'a [u8],
184+
) -> Result<Self, PatternOrUtf8Error<B::StoreFromBytesError>> {
185+
let store = B::try_store_from_bytes(bytes).map_err(PatternOrUtf8Error::Utf8)?;
186+
B::validate_store(store).map_err(PatternOrUtf8Error::Pattern)?;
187+
Ok(Self {
188+
_backend: PhantomData,
189+
store,
190+
})
191+
}
192+
}
193+
170194
#[cfg(feature = "alloc")]
171195
impl<B> Pattern<B, <B::Store as ToOwned>::Owned>
172196
where

utils/pattern/src/frontend/serde.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
1212
type HumanReadablePattern<'a, B> =
1313
Vec<PatternItemCow<'a, <B as PatternBackend>::PlaceholderKeyCow<'a>>>;
1414

15-
impl<'de, 'data, B, Store> Deserialize<'de> for Pattern<B, Store>
15+
impl<'de, 'data, B, Store, E> Deserialize<'de> for Pattern<B, Store>
1616
where
1717
'de: 'data,
18-
B: PatternBackend,
18+
B: PatternBackend<StoreFromBytesError = E>,
1919
B::Store: ToOwned + 'de,
2020
&'de B::Store: Deserialize<'de>,
2121
B::PlaceholderKeyCow<'data>: Deserialize<'de>,

utils/pattern/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub use common::PATTERN_PLACEHOLDER_PART;
7070
pub use double::DoublePlaceholder;
7171
pub use double::DoublePlaceholderKey;
7272
pub use error::PatternError;
73+
pub use error::PatternOrUtf8Error;
7374
pub use frontend::Pattern;
7475
pub use multi_named::MissingNamedPlaceholderError;
7576
pub use multi_named::MultiNamedPlaceholder;

utils/pattern/src/multi_named.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#[cfg(feature = "alloc")]
88
use alloc::{borrow::Cow, collections::BTreeMap, str::FromStr, string::String};
99
use core::fmt;
10+
use core::str::Utf8Error;
1011
#[cfg(feature = "litemap")]
1112
use litemap::LiteMap;
1213
use writeable::Writeable;
@@ -314,9 +315,15 @@ impl PatternBackend for MultiNamedPlaceholder {
314315
#[cfg(feature = "alloc")]
315316
type PlaceholderKeyCow<'a> = MultiNamedPlaceholderKeyCow<'a>;
316317
type Error<'a> = MissingNamedPlaceholderError<'a>;
318+
type StoreFromBytesError = Utf8Error;
317319
type Store = str;
318320
type Iter<'a> = MultiNamedPlaceholderPatternIterator<'a>;
319321

322+
#[inline]
323+
fn try_store_from_bytes(bytes: &[u8]) -> Result<&Self::Store, Self::StoreFromBytesError> {
324+
core::str::from_utf8(bytes)
325+
}
326+
320327
fn validate_store(store: &Self::Store) -> Result<(), Error> {
321328
let mut iter = MultiNamedPlaceholderPatternIterator::new(store);
322329
while iter

utils/pattern/src/single.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//! Code for the [`SinglePlaceholder`] pattern backend.
66
77
use core::convert::Infallible;
8+
use core::str::Utf8Error;
89
use core::{cmp::Ordering, str::FromStr};
910
use writeable::adapters::WriteableAsTryWriteableInfallible;
1011
use writeable::Writeable;
@@ -171,9 +172,15 @@ impl PatternBackend for SinglePlaceholder {
171172
#[cfg(feature = "alloc")]
172173
type PlaceholderKeyCow<'a> = SinglePlaceholderKey;
173174
type Error<'a> = Infallible;
175+
type StoreFromBytesError = Utf8Error;
174176
type Store = str;
175177
type Iter<'a> = SinglePlaceholderPatternIterator<'a>;
176178

179+
#[inline]
180+
fn try_store_from_bytes(utf8: &[u8]) -> Result<&Self::Store, Self::StoreFromBytesError> {
181+
core::str::from_utf8(utf8)
182+
}
183+
177184
fn validate_store(store: &Self::Store) -> Result<(), Error> {
178185
let placeholder_offset_char = store.chars().next().ok_or(Error::InvalidPattern)?;
179186
let initial_offset = placeholder_offset_char.len_utf8();

0 commit comments

Comments
 (0)