Skip to content

Commit

Permalink
Add more impls to Pattern (#4658)
Browse files Browse the repository at this point in the history
This adds Databake, Yokeable, and ZeroFrom. Serde will follow.
  • Loading branch information
sffc committed Mar 7, 2024
1 parent 74f6687 commit 1734713
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 2 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions utils/pattern/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ all-features = true
[dependencies]
displaydoc = { version = "0.2.3", default-features = false }
writeable = { workspace = true }
databake = { workspace = true, features = ["derive"], optional = true }
yoke = { workspace = true, features = ["derive"], optional = true }
zerofrom = { workspace = true, features = ["derive"], optional = true }

[dev-dependencies]
zerofrom = { workspace = true, features = ["alloc"] }
zerovec = { workspace = true, features = ["databake", "serde"] }

[features]
alloc = []
std = ["alloc"]
databake = ["dep:databake"]
yoke = ["dep:yoke"]
zerofrom = ["dep:zerofrom"]
2 changes: 2 additions & 0 deletions utils/pattern/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub enum PatternItem<'a, T> {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_enums)] // Part of core data model
#[cfg(feature = "alloc")]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub enum PatternItemCow<'a, T> {
/// A placeholder of the type specified on this [`PatternItemCow`].
Placeholder(T),
Expand All @@ -40,6 +41,7 @@ pub enum PatternItemCow<'a, T> {
/// 1. Between the start of the string and the first placeholder (prefix)
/// 2. Between two placeholders (infix)
/// 3. Between the final placeholder and the end of the string (suffix)
#[cfg_attr(feature = "serde", serde(borrow))]
Literal(Cow<'a, str>),
}

Expand Down
47 changes: 47 additions & 0 deletions utils/pattern/src/frontend/databake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use core::any::TypeId;

use crate::SinglePlaceholder;

use super::*;
use ::databake::{quote, Bake, CrateEnv, TokenStream};

impl<B, Store> Bake for Pattern<B, Store>
where
B: 'static,
Store: Bake,
{
fn bake(&self, ctx: &CrateEnv) -> TokenStream {
ctx.insert("icu_pattern");
let store = self.store.bake(ctx);
let b = if TypeId::of::<B>() == TypeId::of::<SinglePlaceholder>() {
quote!(icu_pattern::SinglePlaceholder)
} else {
unreachable!("all impls of sealed trait PatternBackend should be covered")
};
quote! {
icu_pattern::Pattern::<#b, _>::from_store_unchecked(#store)
}
}
}

#[test]
/*
Test currently ignored because test_bake is broken:
left: "icu_pattern :: Pattern :: < icu_pattern :: SinglePlaceholder , _ > :: from_store_unchecked (alloc :: borrow :: Cow :: Borrowed (\"\"))"
right: "icu_pattern :: Pattern ::< icu_pattern :: SinglePlaceholder , _ >:: from_store_unchecked (alloc :: borrow :: Cow :: Borrowed (\"\"))"
*/
#[ignore]
fn test_baked() {
use ::databake::test_bake;
use alloc::borrow::Cow;
test_bake!(
Pattern<SinglePlaceholder, Cow<str>>,
const: crate::Pattern::<crate::SinglePlaceholder, _>::from_store_unchecked(alloc::borrow::Cow::Borrowed("")),
icu_pattern
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

#[cfg(feature = "databake")]
mod databake;

use core::{
fmt::{self, Write},
marker::PhantomData,
Expand Down Expand Up @@ -41,7 +44,13 @@ use alloc::{borrow::ToOwned, str::FromStr, string::String};
/// - `Cow<str>` for an owned-or-borrowed pattern
///
/// [`SinglePlaceholder`]: crate::SinglePlaceholder
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
#[cfg_attr(
feature = "zerofrom",
derive(zerofrom::ZeroFrom),
zerofrom(may_borrow(Store))
)]
pub struct Pattern<Backend, Store: ?Sized> {
_backend: PhantomData<Backend>,
store: Store,
Expand All @@ -51,6 +60,42 @@ impl<Backend, Store> Pattern<Backend, Store> {
pub fn take_store(self) -> Store {
self.store
}

/// Creates a pattern from a serialized backing store without checking invariants.
/// Most users should prefer [`Pattern::try_from_store()`].
///
/// The store is expected to come from a valid `Pattern` with this `Backend`,
/// such as by calling [`Pattern::take_store()`]. If the store is not valid,
/// unexpected behavior may occur.
///
/// To parse a pattern string, use [`Self::try_from_str()`].
///
/// # Examples
///
/// ```
/// use icu_pattern::Pattern;
/// use icu_pattern::SinglePlaceholder;
/// use writeable::assert_writeable_eq;
///
/// // Create a pattern from a valid string:
/// let allocated_pattern: Pattern<SinglePlaceholder, String> =
/// Pattern::try_from_str("{0} days").expect("valid pattern");
///
/// // Transform the store and create a new Pattern. This is valid because
/// // we call `.take_store()` and `.from_store_unchecked()` on patterns
/// // with the same backend (`SinglePlaceholder`).
/// let store = allocated_pattern.take_store();
/// let borrowed_pattern: Pattern<SinglePlaceholder, &str> =
/// Pattern::from_store_unchecked(&store);
///
/// assert_writeable_eq!(borrowed_pattern.interpolate([5]), "5 days");
/// ```
pub const fn from_store_unchecked(store: Store) -> Self {
Self {
_backend: PhantomData,
store,
}
}
}

impl<B, Store> Pattern<B, Store>
Expand Down
10 changes: 10 additions & 0 deletions utils/pattern/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,13 @@ mod private {
/// assert_writeable_eq!(pattern.interpolate(["Alice"]), "Hello, Alice!");
/// ```
pub type SinglePlaceholderPattern<Store> = Pattern<SinglePlaceholder, Store>;

#[test]
#[cfg(feature = "alloc")]
fn test_single_placeholder_pattern_impls() {
let a = SinglePlaceholderPattern::try_from_str("{0}").unwrap();
let b = SinglePlaceholderPattern::try_from_str("{0}").unwrap();
assert_eq!(a, b);
let c = b.clone();
assert_eq!(a, c);
}
2 changes: 1 addition & 1 deletion utils/pattern/src/single.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ where
/// ```
///
/// [`Pattern::interpolate()`]: crate::Pattern::interpolate
#[derive(Debug)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(clippy::exhaustive_enums)] // Empty Enum
pub enum SinglePlaceholder {}

Expand Down
41 changes: 41 additions & 0 deletions utils/pattern/tests/derive_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

#![allow(non_camel_case_types, non_snake_case)]

extern crate alloc;

use alloc::borrow::Cow;
use icu_pattern::{Pattern, SinglePlaceholder};

#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
#[cfg_attr(feature = "zerofrom", derive(zerofrom::ZeroFrom))]
// #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = crate))]
struct DeriveTest_SinglePlaceholderPattern_ZeroVec<'data> {
// #[cfg_attr(feature = "serde", serde(borrow))]
_data: Pattern<SinglePlaceholder, Cow<'data, str>>,
}

#[test]
/*
Test currently ignored because test_bake is broken:
left: "crate :: DeriveTest_SinglePlaceholderPattern_ZeroVec { _data : icu_pattern :: Pattern :: < icu_pattern :: SinglePlaceholder , _ > :: from_store_unchecked (alloc :: borrow :: Cow :: Borrowed (\"\")) , }"
right: "crate :: DeriveTest_SinglePlaceholderPattern_ZeroVec { _data : icu_pattern :: Pattern ::< icu_pattern :: SinglePlaceholder , _ >:: from_store_unchecked (alloc :: borrow :: Cow :: Borrowed (\"\")) , }"
*/
#[ignore]
#[cfg(all(feature = "databake", feature = "alloc"))]
fn bake_SinglePlaceholderPattern_ZeroVec() {
use databake::*;
extern crate std;
test_bake!(
DeriveTest_SinglePlaceholderPattern_ZeroVec<'static>,
crate::DeriveTest_SinglePlaceholderPattern_ZeroVec {
_data: icu_pattern::Pattern::<icu_pattern::SinglePlaceholder, _>::from_store_unchecked(
alloc::borrow::Cow::Borrowed(""),
)
},
);
}

0 comments on commit 1734713

Please sign in to comment.