Skip to content

Commit

Permalink
Add PluralElementsPackedULE and use it in relativetime
Browse files Browse the repository at this point in the history
  • Loading branch information
sffc committed Sep 9, 2024
1 parent 79582ad commit 0f7223b
Show file tree
Hide file tree
Showing 7 changed files with 730 additions and 164 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

106 changes: 48 additions & 58 deletions components/experimental/src/relativetime/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ use core::{fmt::Debug, str::FromStr};
use icu_pattern::{Pattern, PatternBackend, SinglePlaceholder};
#[cfg(feature = "datagen")]
use icu_plurals::PluralElements;
use icu_plurals::{PluralCategory, PluralOperands, PluralRules};
use icu_plurals::{
provider::{FourBitMetadata, PluralElementsPackedULE},
PluralCategory, PluralOperands, PluralRules,
};
use icu_provider::prelude::*;
use zerovec::ZeroMap;

Expand Down Expand Up @@ -103,7 +106,7 @@ pub struct PluralCategoryStr<'data>(pub PluralCategory, pub Cow<'data, str>);
pub struct PluralPatterns<'data, B> {
#[cfg_attr(feature = "serde", serde(borrow))]
#[doc(hidden)] // databake only
pub strings: icu_plurals::provider::PluralElementsV1<'data>,
pub strings: Cow<'data, icu_plurals::provider::PluralElementsPackedULE<str>>,
#[cfg_attr(feature = "serde", serde(skip))]
#[doc(hidden)] // databake only
pub _phantom: PhantomData<B>,
Expand All @@ -121,76 +124,63 @@ impl<'data, B> Clone for PluralPatterns<'data, B> {
impl<'data, B: PatternBackend<Store = str>> PluralPatterns<'data, B> {
/// Returns the pattern for the given [`PluralCategory`].
pub fn get(&'data self, op: PluralOperands, rules: &PluralRules) -> &'data Pattern<B, str> {
Pattern::from_ref_store_unchecked(self.strings.get(op, rules))
Pattern::from_ref_store_unchecked(self.strings.get(op, rules).1)
}
}

#[cfg(feature = "datagen")]
impl<'data, B: PatternBackend<Store = str>> TryFrom<PluralElements<'data, str>>
impl<'data, B: PatternBackend<Store = str>> TryFrom<PluralElements<&'data str>>
for PluralPatterns<'static, B>
where
B::PlaceholderKeyCow<'data>: FromStr,
<B::PlaceholderKeyCow<'data> as FromStr>::Err: Debug,
{
type Error = icu_pattern::PatternError;

fn try_from(elements: PluralElements<str>) -> Result<Self, Self::Error> {
let make_pattern = |s: &str|
fn try_from(elements: PluralElements<&'data str>) -> Result<Self, Self::Error> {
let make_pattern = |s: &&str|
// TODO: Make pattern support apostrophes
Pattern::<B, String>::from_str(&s.replace('\'', "''")).map(|p| p.take_store());
Pattern::<B, String>::from_str(&s.replace('\'', "''")).map(|p| (FourBitMetadata::zero(), p.take_store()));

let plural_elements = PluralElements::new(make_pattern(elements.other())?)
.with_zero_value(
Some(elements.zero())
.filter(|&e| e != elements.other())
.map(make_pattern)
.transpose()?,
)
.with_one_value(
Some(elements.one())
.filter(|&e| e != elements.other())
.map(make_pattern)
.transpose()?,
)
.with_two_value(
Some(elements.two())
.filter(|&e| e != elements.other())
.map(make_pattern)
.transpose()?,
)
.with_few_value(
Some(elements.few())
.filter(|&e| e != elements.other())
.map(make_pattern)
.transpose()?,
)
.with_many_value(
Some(elements.many())
.filter(|&e| e != elements.other())
.map(make_pattern)
.transpose()?,
)
.with_explicit_zero_value(elements.explicit_zero().map(make_pattern).transpose()?)
.with_explicit_one_value(elements.explicit_one().map(make_pattern).transpose()?);

let packed_pattern_box =
zerovec::ule::encode_varule_to_box::<_, PluralElementsPackedULE<str>>(&plural_elements);

Ok(Self {
strings: PluralElements::new(make_pattern(elements.other())?.as_str())
.with_zero_value(
Some(elements.zero())
.filter(|&e| e != elements.other())
.map(make_pattern)
.transpose()?
.as_deref(),
)
.with_one_value(
Some(elements.one())
.filter(|&e| e != elements.other())
.map(make_pattern)
.transpose()?
.as_deref(),
)
.with_two_value(
Some(elements.two())
.filter(|&e| e != elements.other())
.map(make_pattern)
.transpose()?
.as_deref(),
)
.with_few_value(
Some(elements.few())
.filter(|&e| e != elements.other())
.map(make_pattern)
.transpose()?
.as_deref(),
)
.with_many_value(
Some(elements.many())
.filter(|&e| e != elements.other())
.map(make_pattern)
.transpose()?
.as_deref(),
)
.with_explicit_zero_value(
elements
.explicit_zero()
.map(make_pattern)
.transpose()?
.as_deref(),
)
.with_explicit_one_value(
elements
.explicit_one()
.map(make_pattern)
.transpose()?
.as_deref(),
)
.into(),
strings: Cow::Owned(packed_pattern_box),
_phantom: PhantomData,
})
}
Expand Down
1 change: 1 addition & 0 deletions components/plurals/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ icu_benchmark_macros = { path = "../../tools/benchmark/macros" }
icu_locale_core = { path = "../../components/locale_core" }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
postcard = { workspace = true, features = ["alloc"] }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = { workspace = true }
Expand Down
146 changes: 83 additions & 63 deletions components/plurals/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -877,21 +877,29 @@ where
}

#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
/// A bag of values for different plural cases.
pub struct PluralElements<'a, T: ?Sized> {
zero: Option<&'a T>,
one: Option<&'a T>,
two: Option<&'a T>,
few: Option<&'a T>,
many: Option<&'a T>,
other: &'a T,
explicit_zero: Option<&'a T>,
explicit_one: Option<&'a T>,
pub struct PluralElements<T> {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
zero: Option<T>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
one: Option<T>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
two: Option<T>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
few: Option<T>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
many: Option<T>,
other: T,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
explicit_zero: Option<T>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
explicit_one: Option<T>,
}

impl<'a, T: ?Sized + PartialEq> PluralElements<'a, T> {
impl<T> PluralElements<T> {
/// Creates a new [`PluralElements`] with the given default value.
pub fn new(other: &'a T) -> Self {
pub fn new(other: T) -> Self {
Self {
other,
zero: None,
Expand All @@ -904,99 +912,111 @@ impl<'a, T: ?Sized + PartialEq> PluralElements<'a, T> {
}
}

/// The value for [`PluralCategory::Zero`]
pub fn zero(&self) -> &T {
self.zero.as_ref().unwrap_or(&self.other)
}

/// The value for [`PluralCategory::One`]
pub fn one(&self) -> &T {
self.one.as_ref().unwrap_or(&self.other)
}

/// The value for [`PluralCategory::Two`]
pub fn two(&self) -> &T {
self.two.as_ref().unwrap_or(&self.other)
}

/// The value for [`PluralCategory::Few`]
pub fn few(&self) -> &T {
self.few.as_ref().unwrap_or(&self.other)
}

/// The value for [`PluralCategory::Many`]
pub fn many(&self) -> &T {
self.many.as_ref().unwrap_or(&self.other)
}

/// The value for [`PluralCategory::Other`]
pub fn other(&self) -> &T {
&self.other
}

/// The value used when the [`PluralOperands`] are exactly 0.
pub fn explicit_zero(&self) -> Option<&T> {
self.explicit_zero.as_ref()
}

/// The value used when the [`PluralOperands`] are exactly 1.
pub fn explicit_one(&self) -> Option<&T> {
self.explicit_one.as_ref()
}

pub(crate) fn has_specials(&self) -> bool {
self.zero.is_some()
|| self.one.is_some()
|| self.two.is_some()
|| self.few.is_some()
|| self.many.is_some()
|| self.explicit_zero.is_some()
|| self.explicit_one.is_some()
}
}

impl<T: PartialEq> PluralElements<T> {
/// Sets the value for [`PluralCategory::Zero`].
pub fn with_zero_value(self, zero: Option<&'a T>) -> Self {
pub fn with_zero_value(self, zero: Option<T>) -> Self {
Self {
zero: zero.filter(|&t| t != self.other),
zero: zero.filter(|t| *t != self.other),
..self
}
}

/// Sets the value for [`PluralCategory::One`].
pub fn with_one_value(self, one: Option<&'a T>) -> Self {
pub fn with_one_value(self, one: Option<T>) -> Self {
Self {
one: one.filter(|&t| t != self.other),
one: one.filter(|t| *t != self.other),
..self
}
}

/// Sets the value for [`PluralCategory::Two`].
pub fn with_two_value(self, two: Option<&'a T>) -> Self {
pub fn with_two_value(self, two: Option<T>) -> Self {
Self {
two: two.filter(|&t| t != self.other),
two: two.filter(|t| *t != self.other),
..self
}
}

/// Sets the value for [`PluralCategory::Few`].
pub fn with_few_value(self, few: Option<&'a T>) -> Self {
pub fn with_few_value(self, few: Option<T>) -> Self {
Self {
few: few.filter(|&t| t != self.other),
few: few.filter(|t| *t != self.other),
..self
}
}

/// Sets the value for [`PluralCategory::Many`].
pub fn with_many_value(self, many: Option<&'a T>) -> Self {
pub fn with_many_value(self, many: Option<T>) -> Self {
Self {
many: many.filter(|&t| t != self.other),
many: many.filter(|t| *t != self.other),
..self
}
}

/// Sets the value for explicit 0.
pub fn with_explicit_zero_value(self, explicit_zero: Option<&'a T>) -> Self {
pub fn with_explicit_zero_value(self, explicit_zero: Option<T>) -> Self {
Self {
explicit_zero,
..self
}
}

/// Sets the value for explicit 1.
pub fn with_explicit_one_value(self, explicit_one: Option<&'a T>) -> Self {
pub fn with_explicit_one_value(self, explicit_one: Option<T>) -> Self {
Self {
explicit_one,
..self
}
}

/// The value for [`PluralCategory::Zero`]
pub fn zero(&self) -> &'a T {
self.zero.unwrap_or(self.other)
}

/// The value for [`PluralCategory::One`]
pub fn one(&self) -> &'a T {
self.one.unwrap_or(self.other)
}

/// The value for [`PluralCategory::Two`]
pub fn two(&self) -> &'a T {
self.two.unwrap_or(self.other)
}

/// The value for [`PluralCategory::Few`]
pub fn few(&self) -> &'a T {
self.few.unwrap_or(self.other)
}

/// The value for [`PluralCategory::Many`]
pub fn many(&self) -> &'a T {
self.many.unwrap_or(self.other)
}

/// The value for [`PluralCategory::Other`]
pub fn other(&self) -> &'a T {
self.other
}

/// The value used when the [`PluralOperands`] are exactly 0.
pub fn explicit_zero(&self) -> Option<&'a T> {
self.explicit_zero
}

/// The value used when the [`PluralOperands`] are exactly 1.
pub fn explicit_one(&self) -> Option<&'a T> {
self.explicit_one
}
}
Loading

0 comments on commit 0f7223b

Please sign in to comment.