summaryrefslogtreecommitdiffstats
path: root/vendor/fluent-bundle/src/types
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/fluent-bundle/src/types
parentInitial commit. (diff)
downloadrustc-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.rs202
-rw-r--r--vendor/fluent-bundle/src/types/number.rs252
-rw-r--r--vendor/fluent-bundle/src/types/plural.rs22
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)?))
+ }
+}