diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/fluent-bundle/src/types | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/fluent-bundle/src/types')
-rw-r--r-- | vendor/fluent-bundle/src/types/mod.rs | 202 | ||||
-rw-r--r-- | vendor/fluent-bundle/src/types/number.rs | 252 | ||||
-rw-r--r-- | vendor/fluent-bundle/src/types/plural.rs | 22 |
3 files changed, 476 insertions, 0 deletions
diff --git a/vendor/fluent-bundle/src/types/mod.rs b/vendor/fluent-bundle/src/types/mod.rs new file mode 100644 index 000000000..714fe4c76 --- /dev/null +++ b/vendor/fluent-bundle/src/types/mod.rs @@ -0,0 +1,202 @@ +//! `types` module contains types necessary for Fluent runtime +//! value handling. +//! The core struct is [`FluentValue`] which is a type that can be passed +//! to the [`FluentBundle::format_pattern`](crate::bundle::FluentBundle) as an argument, it can be passed +//! to any Fluent Function, and any function may return it. +//! +//! This part of functionality is not fully hashed out yet, since we're waiting +//! for the internationalization APIs to mature, at which point all number +//! formatting operations will be moved out of Fluent. +//! +//! For now, [`FluentValue`] can be a string, a number, or a custom [`FluentType`] +//! which allows users of the library to implement their own types of values, +//! such as dates, or more complex structures needed for their bindings. +mod number; +mod plural; + +pub use number::*; +use plural::PluralRules; + +use std::any::Any; +use std::borrow::{Borrow, Cow}; +use std::fmt; +use std::str::FromStr; + +use intl_pluralrules::{PluralCategory, PluralRuleType}; + +use crate::memoizer::MemoizerKind; +use crate::resolver::Scope; +use crate::resource::FluentResource; + +pub trait FluentType: fmt::Debug + AnyEq + 'static { + fn duplicate(&self) -> Box<dyn FluentType + Send>; + fn as_string(&self, intls: &intl_memoizer::IntlLangMemoizer) -> Cow<'static, str>; + fn as_string_threadsafe( + &self, + intls: &intl_memoizer::concurrent::IntlLangMemoizer, + ) -> Cow<'static, str>; +} + +impl PartialEq for dyn FluentType + Send { + fn eq(&self, other: &Self) -> bool { + self.equals(other.as_any()) + } +} + +pub trait AnyEq: Any + 'static { + fn equals(&self, other: &dyn Any) -> bool; + fn as_any(&self) -> &dyn Any; +} + +impl<T: Any + PartialEq> AnyEq for T { + fn equals(&self, other: &dyn Any) -> bool { + other + .downcast_ref::<Self>() + .map_or(false, |that| self == that) + } + fn as_any(&self) -> &dyn Any { + self + } +} + +/// The `FluentValue` enum represents values which can be formatted to a String. +/// +/// Those values are either passed as arguments to [`FluentBundle::format_pattern`][] or +/// produced by functions, or generated in the process of pattern resolution. +/// +/// [`FluentBundle::format_pattern`]: ../bundle/struct.FluentBundle.html#method.format_pattern +#[derive(Debug)] +pub enum FluentValue<'source> { + String(Cow<'source, str>), + Number(FluentNumber), + Custom(Box<dyn FluentType + Send>), + None, + Error, +} + +impl<'s> PartialEq for FluentValue<'s> { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (FluentValue::String(s), FluentValue::String(s2)) => s == s2, + (FluentValue::Number(s), FluentValue::Number(s2)) => s == s2, + (FluentValue::Custom(s), FluentValue::Custom(s2)) => s == s2, + _ => false, + } + } +} + +impl<'s> Clone for FluentValue<'s> { + fn clone(&self) -> Self { + match self { + FluentValue::String(s) => FluentValue::String(s.clone()), + FluentValue::Number(s) => FluentValue::Number(s.clone()), + FluentValue::Custom(s) => { + let new_value: Box<dyn FluentType + Send> = s.duplicate(); + FluentValue::Custom(new_value) + } + FluentValue::Error => FluentValue::Error, + FluentValue::None => FluentValue::None, + } + } +} + +impl<'source> FluentValue<'source> { + pub fn try_number<S: ToString>(v: S) -> Self { + let s = v.to_string(); + if let Ok(num) = FluentNumber::from_str(&s) { + num.into() + } else { + s.into() + } + } + + pub fn matches<R: Borrow<FluentResource>, M>( + &self, + other: &FluentValue, + scope: &Scope<R, M>, + ) -> bool + where + M: MemoizerKind, + { + match (self, other) { + (&FluentValue::String(ref a), &FluentValue::String(ref b)) => a == b, + (&FluentValue::Number(ref a), &FluentValue::Number(ref b)) => a == b, + (&FluentValue::String(ref a), &FluentValue::Number(ref b)) => { + let cat = match a.as_ref() { + "zero" => PluralCategory::ZERO, + "one" => PluralCategory::ONE, + "two" => PluralCategory::TWO, + "few" => PluralCategory::FEW, + "many" => PluralCategory::MANY, + "other" => PluralCategory::OTHER, + _ => return false, + }; + scope + .bundle + .intls + .with_try_get_threadsafe::<PluralRules, _, _>( + (PluralRuleType::CARDINAL,), + |pr| pr.0.select(b) == Ok(cat), + ) + .unwrap() + } + _ => false, + } + } + + pub fn write<W, R, M>(&self, w: &mut W, scope: &Scope<R, M>) -> fmt::Result + where + W: fmt::Write, + R: Borrow<FluentResource>, + M: MemoizerKind, + { + if let Some(formatter) = &scope.bundle.formatter { + if let Some(val) = formatter(self, &scope.bundle.intls) { + return w.write_str(&val); + } + } + match self { + FluentValue::String(s) => w.write_str(s), + FluentValue::Number(n) => w.write_str(&n.as_string()), + FluentValue::Custom(s) => w.write_str(&scope.bundle.intls.stringify_value(&**s)), + FluentValue::Error => Ok(()), + FluentValue::None => Ok(()), + } + } + + pub fn as_string<R: Borrow<FluentResource>, M>(&self, scope: &Scope<R, M>) -> Cow<'source, str> + where + M: MemoizerKind, + { + if let Some(formatter) = &scope.bundle.formatter { + if let Some(val) = formatter(self, &scope.bundle.intls) { + return val.into(); + } + } + match self { + FluentValue::String(s) => s.clone(), + FluentValue::Number(n) => n.as_string(), + FluentValue::Custom(s) => scope.bundle.intls.stringify_value(&**s), + FluentValue::Error => "".into(), + FluentValue::None => "".into(), + } + } +} + +impl<'source> From<String> for FluentValue<'source> { + fn from(s: String) -> Self { + FluentValue::String(s.into()) + } +} + +impl<'source> From<&'source str> for FluentValue<'source> { + fn from(s: &'source str) -> Self { + FluentValue::String(s.into()) + } +} + +impl<'source> From<Cow<'source, str>> for FluentValue<'source> { + fn from(s: Cow<'source, str>) -> Self { + FluentValue::String(s) + } +} diff --git a/vendor/fluent-bundle/src/types/number.rs b/vendor/fluent-bundle/src/types/number.rs new file mode 100644 index 000000000..d39291ff4 --- /dev/null +++ b/vendor/fluent-bundle/src/types/number.rs @@ -0,0 +1,252 @@ +use std::borrow::Cow; +use std::convert::TryInto; +use std::default::Default; +use std::str::FromStr; + +use intl_pluralrules::operands::PluralOperands; + +use crate::args::FluentArgs; +use crate::types::FluentValue; + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum FluentNumberStyle { + Decimal, + Currency, + Percent, +} + +impl std::default::Default for FluentNumberStyle { + fn default() -> Self { + Self::Decimal + } +} + +impl From<&str> for FluentNumberStyle { + fn from(input: &str) -> Self { + match input { + "decimal" => Self::Decimal, + "currency" => Self::Currency, + "percent" => Self::Percent, + _ => Self::default(), + } + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum FluentNumberCurrencyDisplayStyle { + Symbol, + Code, + Name, +} + +impl std::default::Default for FluentNumberCurrencyDisplayStyle { + fn default() -> Self { + Self::Symbol + } +} + +impl From<&str> for FluentNumberCurrencyDisplayStyle { + fn from(input: &str) -> Self { + match input { + "symbol" => Self::Symbol, + "code" => Self::Code, + "name" => Self::Name, + _ => Self::default(), + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct FluentNumberOptions { + pub style: FluentNumberStyle, + pub currency: Option<String>, + pub currency_display: FluentNumberCurrencyDisplayStyle, + pub use_grouping: bool, + pub minimum_integer_digits: Option<usize>, + pub minimum_fraction_digits: Option<usize>, + pub maximum_fraction_digits: Option<usize>, + pub minimum_significant_digits: Option<usize>, + pub maximum_significant_digits: Option<usize>, +} + +impl Default for FluentNumberOptions { + fn default() -> Self { + Self { + style: Default::default(), + currency: None, + currency_display: Default::default(), + use_grouping: true, + minimum_integer_digits: None, + minimum_fraction_digits: None, + maximum_fraction_digits: None, + minimum_significant_digits: None, + maximum_significant_digits: None, + } + } +} + +impl FluentNumberOptions { + pub fn merge(&mut self, opts: &FluentArgs) { + for (key, value) in opts.iter() { + match (key, value) { + ("style", FluentValue::String(n)) => { + self.style = n.as_ref().into(); + } + ("currency", FluentValue::String(n)) => { + self.currency = Some(n.to_string()); + } + ("currencyDisplay", FluentValue::String(n)) => { + self.currency_display = n.as_ref().into(); + } + ("useGrouping", FluentValue::String(n)) => { + self.use_grouping = n != "false"; + } + ("minimumIntegerDigits", FluentValue::Number(n)) => { + self.minimum_integer_digits = Some(n.into()); + } + ("minimumFractionDigits", FluentValue::Number(n)) => { + self.minimum_fraction_digits = Some(n.into()); + } + ("maximumFractionDigits", FluentValue::Number(n)) => { + self.maximum_fraction_digits = Some(n.into()); + } + ("minimumSignificantDigits", FluentValue::Number(n)) => { + self.minimum_significant_digits = Some(n.into()); + } + ("maximumSignificantDigits", FluentValue::Number(n)) => { + self.maximum_significant_digits = Some(n.into()); + } + _ => {} + } + } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct FluentNumber { + pub value: f64, + pub options: FluentNumberOptions, +} + +impl FluentNumber { + pub const fn new(value: f64, options: FluentNumberOptions) -> Self { + Self { value, options } + } + + pub fn as_string(&self) -> Cow<'static, str> { + let mut val = self.value.to_string(); + if let Some(minfd) = self.options.minimum_fraction_digits { + if let Some(pos) = val.find('.') { + let frac_num = val.len() - pos - 1; + let missing = if frac_num > minfd { + 0 + } else { + minfd - frac_num + }; + val = format!("{}{}", val, "0".repeat(missing)); + } else { + val = format!("{}.{}", val, "0".repeat(minfd)); + } + } + val.into() + } +} + +impl FromStr for FluentNumber { + type Err = std::num::ParseFloatError; + + fn from_str(input: &str) -> Result<Self, Self::Err> { + f64::from_str(input).map(|n| { + let mfd = input.find('.').map(|pos| input.len() - pos - 1); + let opts = FluentNumberOptions { + minimum_fraction_digits: mfd, + ..Default::default() + }; + Self::new(n, opts) + }) + } +} + +impl<'l> From<FluentNumber> for FluentValue<'l> { + fn from(input: FluentNumber) -> Self { + FluentValue::Number(input) + } +} + +macro_rules! from_num { + ($num:ty) => { + impl From<$num> for FluentNumber { + fn from(n: $num) -> Self { + Self { + value: n as f64, + options: FluentNumberOptions::default(), + } + } + } + impl From<&$num> for FluentNumber { + fn from(n: &$num) -> Self { + Self { + value: *n as f64, + options: FluentNumberOptions::default(), + } + } + } + impl From<FluentNumber> for $num { + fn from(input: FluentNumber) -> Self { + input.value as $num + } + } + impl From<&FluentNumber> for $num { + fn from(input: &FluentNumber) -> Self { + input.value as $num + } + } + impl From<$num> for FluentValue<'_> { + fn from(n: $num) -> Self { + FluentValue::Number(n.into()) + } + } + impl From<&$num> for FluentValue<'_> { + fn from(n: &$num) -> Self { + FluentValue::Number(n.into()) + } + } + }; + ($($num:ty)+) => { + $(from_num!($num);)+ + }; +} + +impl From<&FluentNumber> for PluralOperands { + fn from(input: &FluentNumber) -> Self { + let mut operands: Self = input + .value + .try_into() + .expect("Failed to generate operands out of FluentNumber"); + if let Some(mfd) = input.options.minimum_fraction_digits { + if mfd > operands.v { + operands.f *= 10_u64.pow(mfd as u32 - operands.v as u32); + operands.v = mfd; + } + } + // XXX: Add support for other options. + operands + } +} + +from_num!(i8 i16 i32 i64 i128 isize); +from_num!(u8 u16 u32 u64 u128 usize); +from_num!(f32 f64); + +#[cfg(test)] +mod tests { + use crate::types::FluentValue; + + #[test] + fn value_from_copy_ref() { + let x = 1i16; + let y = &x; + let z: FluentValue = y.into(); + assert_eq!(z, FluentValue::try_number(1)); + } +} diff --git a/vendor/fluent-bundle/src/types/plural.rs b/vendor/fluent-bundle/src/types/plural.rs new file mode 100644 index 000000000..1151fd6d3 --- /dev/null +++ b/vendor/fluent-bundle/src/types/plural.rs @@ -0,0 +1,22 @@ +use fluent_langneg::{negotiate_languages, NegotiationStrategy}; +use intl_memoizer::Memoizable; +use intl_pluralrules::{PluralRuleType, PluralRules as IntlPluralRules}; +use unic_langid::LanguageIdentifier; + +pub struct PluralRules(pub IntlPluralRules); + +impl Memoizable for PluralRules { + type Args = (PluralRuleType,); + type Error = &'static str; + fn construct(lang: LanguageIdentifier, args: Self::Args) -> Result<Self, Self::Error> { + let default_lang: LanguageIdentifier = "en".parse().unwrap(); + let pr_lang = negotiate_languages( + &[lang], + &IntlPluralRules::get_locales(args.0), + Some(&default_lang), + NegotiationStrategy::Lookup, + )[0] + .clone(); + Ok(Self(IntlPluralRules::create(pr_lang, args.0)?)) + } +} |