Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+225
View File
@@ -0,0 +1,225 @@
//! A crate for generating plural rule operands from numberical input.
//!
//! This crate generates plural operands according to the specifications outlined at [Unicode's website](https://unicode.org/reports/tr35/tr35-numbers.html#Operands).
//!
//! Input is supported for int, float, and &str.
//!
//! # Examples
//!
//! Plural rules example for Polish
//!
//! ```
//! use intl_pluralrules::{PluralRules, PluralRuleType, PluralCategory};
//! use unic_langid::LanguageIdentifier;
//!
//! let langid: LanguageIdentifier = "pl".parse().expect("Parsing failed.");
//!
//! assert!(PluralRules::get_locales(PluralRuleType::CARDINAL).contains(&langid));
//!
//! let pr = PluralRules::create(langid.clone(), PluralRuleType::CARDINAL).unwrap();
//! assert_eq!(pr.select(1), Ok(PluralCategory::ONE));
//! assert_eq!(pr.select("3"), Ok(PluralCategory::FEW));
//! assert_eq!(pr.select(12), Ok(PluralCategory::MANY));
//! assert_eq!(pr.select("5.0"), Ok(PluralCategory::OTHER));
//!
//! assert_eq!(pr.get_locale(), &langid);
//! ```
/// A public AST module for plural rule representations.
pub mod operands;
#[cfg(not(tarpaulin_include))]
mod rules;
use std::convert::TryInto;
use unic_langid::LanguageIdentifier;
use crate::operands::PluralOperands;
use crate::rules::*;
/// A public enum for handling the plural category.
/// Each plural category will vary, depending on the language that is being used and whether that language has that plural category.
#[derive(Debug, Eq, PartialEq)]
pub enum PluralCategory {
ZERO,
ONE,
TWO,
FEW,
MANY,
OTHER,
}
/// A public enum for handling plural type.
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub enum PluralRuleType {
/// Ordinal numbers express position or rank in a sequence. [More about oridinal numbers](https://en.wikipedia.org/wiki/Ordinal_number_(linguistics))
ORDINAL,
/// Cardinal numbers are natural numbers. [More about cardinal numbers](https://en.wikipedia.org/wiki/Cardinal_number)
CARDINAL,
}
// pub use rules::PluralRuleType;
/// CLDR_VERSION is the version of CLDR extracted from the file used to generate rules.rs.
pub use crate::rules::CLDR_VERSION;
/// The main structure for selecting plural rules.
///
/// # Examples
///
/// ```
/// use intl_pluralrules::{PluralRules, PluralRuleType, PluralCategory};
/// use unic_langid::LanguageIdentifier;
///
/// let langid: LanguageIdentifier = "naq".parse().expect("Parsing failed.");
/// let pr_naq = PluralRules::create(langid, PluralRuleType::CARDINAL).unwrap();
/// assert_eq!(pr_naq.select(1), Ok(PluralCategory::ONE));
/// assert_eq!(pr_naq.select("2"), Ok(PluralCategory::TWO));
/// assert_eq!(pr_naq.select(5.0), Ok(PluralCategory::OTHER));
/// ```
#[derive(Clone)]
pub struct PluralRules {
locale: LanguageIdentifier,
function: PluralRule,
}
impl PluralRules {
/// Returns an instance of PluralRules.
///
/// # Examples
/// ```
/// use intl_pluralrules::{PluralRules, PluralRuleType, PluralCategory};
/// use unic_langid::LanguageIdentifier;
///
/// let langid: LanguageIdentifier = "naq".parse().expect("Parsing failed.");
/// let pr_naq = PluralRules::create(langid, PluralRuleType::CARDINAL);
/// assert_eq!(pr_naq.is_ok(), !pr_naq.is_err());
///
/// let langid: LanguageIdentifier = "xx".parse().expect("Parsing failed.");
/// let pr_broken = PluralRules::create(langid, PluralRuleType::CARDINAL);
/// assert_eq!(pr_broken.is_err(), !pr_broken.is_ok());
/// ```
pub fn create<L: Into<LanguageIdentifier>>(
langid: L,
prt: PluralRuleType,
) -> Result<Self, &'static str> {
let langid = langid.into();
let returned_rule = match prt {
PluralRuleType::CARDINAL => {
let idx = rules::PRS_CARDINAL.binary_search_by_key(&&langid, |(l, _)| l);
idx.map(|idx| rules::PRS_CARDINAL[idx].1)
}
PluralRuleType::ORDINAL => {
let idx = rules::PRS_ORDINAL.binary_search_by_key(&&langid, |(l, _)| l);
idx.map(|idx| rules::PRS_ORDINAL[idx].1)
}
};
match returned_rule {
Ok(returned_rule) => Ok(Self {
locale: langid,
function: returned_rule,
}),
Err(_) => Err("unknown locale"),
}
}
/// Returns a result of the plural category for the given input.
///
/// If the input is not numeric.
///
/// # Examples
/// ```
/// use intl_pluralrules::{PluralRules, PluralRuleType, PluralCategory};
/// use unic_langid::LanguageIdentifier;
///
/// let langid: LanguageIdentifier = "naq".parse().expect("Parsing failed.");
/// let pr_naq = PluralRules::create(langid, PluralRuleType::CARDINAL).unwrap();
/// assert_eq!(pr_naq.select(1), Ok(PluralCategory::ONE));
/// assert_eq!(pr_naq.select(2), Ok(PluralCategory::TWO));
/// assert_eq!(pr_naq.select(5), Ok(PluralCategory::OTHER));
/// ```
pub fn select<N: TryInto<PluralOperands>>(
&self,
number: N,
) -> Result<PluralCategory, &'static str> {
let ops = number.try_into();
let pr = self.function;
match ops {
Ok(ops) => Ok(pr(&ops)),
Err(_) => Err("Argument can not be parsed to operands."),
}
}
/// Returns a list of the available locales.
///
/// # Examples
/// ```
/// use intl_pluralrules::{PluralRules, PluralRuleType};
///
/// assert_eq!(
/// PluralRules::get_locales(PluralRuleType::CARDINAL).is_empty(),
/// false
/// );
/// ```
pub fn get_locales(prt: PluralRuleType) -> Vec<LanguageIdentifier> {
let prs = match prt {
PluralRuleType::CARDINAL => rules::PRS_CARDINAL,
PluralRuleType::ORDINAL => rules::PRS_ORDINAL,
};
prs.iter().map(|(l, _)| l.clone()).collect()
}
/// Returns the locale name for this PluralRule instance.
///
/// # Examples
/// ```
/// use intl_pluralrules::{PluralRules, PluralRuleType};
/// use unic_langid::LanguageIdentifier;
///
/// let langid: LanguageIdentifier = "naq".parse().expect("Parsing failed.");
/// let pr_naq = PluralRules::create(langid.clone(), PluralRuleType::CARDINAL).unwrap();
/// assert_eq!(pr_naq.get_locale(), &langid);
/// ```
pub fn get_locale(&self) -> &LanguageIdentifier {
&self.locale
}
}
#[cfg(test)]
mod tests {
use super::{PluralCategory, PluralRuleType, PluralRules, CLDR_VERSION};
use unic_langid::LanguageIdentifier;
#[test]
fn cardinals_test() {
let langid: LanguageIdentifier = "naq".parse().expect("Parsing failed.");
let pr_naq = PluralRules::create(langid, PluralRuleType::CARDINAL).unwrap();
assert_eq!(pr_naq.select(1), Ok(PluralCategory::ONE));
assert_eq!(pr_naq.select(2), Ok(PluralCategory::TWO));
assert_eq!(pr_naq.select(5), Ok(PluralCategory::OTHER));
let langid: LanguageIdentifier = "xx".parse().expect("Parsing failed.");
let pr_broken = PluralRules::create(langid, PluralRuleType::CARDINAL);
assert_eq!(pr_broken.is_err(), !pr_broken.is_ok());
}
#[test]
fn ordinals_rules() {
let langid: LanguageIdentifier = "uk".parse().expect("Parsing failed.");
let pr_naq = PluralRules::create(langid, PluralRuleType::ORDINAL).unwrap();
assert_eq!(pr_naq.select(33), Ok(PluralCategory::FEW));
assert_eq!(pr_naq.select(113), Ok(PluralCategory::OTHER));
}
#[test]
fn version_test() {
assert_eq!(CLDR_VERSION, 37);
}
#[test]
fn locale_test() {
assert_eq!(
PluralRules::get_locales(PluralRuleType::CARDINAL).is_empty(),
false
);
}
}
+186
View File
@@ -0,0 +1,186 @@
//! Plural operands in compliance with [CLDR Plural Rules](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules).
//!
//! See [full operands description](https://unicode.org/reports/tr35/tr35-numbers.html#Operands).
//!
//! # Examples
//!
//! From int
//!
//! ```
//! use std::convert::TryFrom;
//! use intl_pluralrules::operands::*;
//! assert_eq!(Ok(PluralOperands {
//! n: 2_f64,
//! i: 2,
//! v: 0,
//! w: 0,
//! f: 0,
//! t: 0,
//! }), PluralOperands::try_from(2))
//! ```
//!
//! From float
//!
//! ```
//! use std::convert::TryFrom;
//! use intl_pluralrules::operands::*;
//! assert_eq!(Ok(PluralOperands {
//! n: 1234.567_f64,
//! i: 1234,
//! v: 3,
//! w: 3,
//! f: 567,
//! t: 567,
//! }), PluralOperands::try_from("-1234.567"))
//! ```
//!
//! From &str
//!
//! ```
//! use std::convert::TryFrom;
//! use intl_pluralrules::operands::*;
//! assert_eq!(Ok(PluralOperands {
//! n: 123.45_f64,
//! i: 123,
//! v: 2,
//! w: 2,
//! f: 45,
//! t: 45,
//! }), PluralOperands::try_from(123.45))
//! ```
#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
use std::convert::TryFrom;
use std::isize;
use std::str::FromStr;
/// A full plural operands representation of a number. See [CLDR Plural Rules](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules) for complete operands description.
#[derive(Debug, PartialEq)]
pub struct PluralOperands {
/// Absolute value of input
pub n: f64,
/// Integer value of input
pub i: u64,
/// Number of visible fraction digits with trailing zeros
pub v: usize,
/// Number of visible fraction digits without trailing zeros
pub w: usize,
/// Visible fraction digits with trailing zeros
pub f: u64,
/// Visible fraction digits without trailing zeros
pub t: u64,
}
impl<'a> TryFrom<&'a str> for PluralOperands {
type Error = &'static str;
fn try_from(input: &'a str) -> Result<Self, Self::Error> {
let abs_str = if input.starts_with('-') {
&input[1..]
} else {
&input
};
let absolute_value = f64::from_str(&abs_str).map_err(|_| "Incorrect number passed!")?;
let integer_digits;
let num_fraction_digits0;
let num_fraction_digits;
let fraction_digits0;
let fraction_digits;
if let Some(dec_pos) = abs_str.find('.') {
let int_str = &abs_str[..dec_pos];
let dec_str = &abs_str[(dec_pos + 1)..];
integer_digits =
u64::from_str(&int_str).map_err(|_| "Could not convert string to integer!")?;
let backtrace = dec_str.trim_end_matches('0');
num_fraction_digits0 = dec_str.len() as usize;
num_fraction_digits = backtrace.len() as usize;
fraction_digits0 =
u64::from_str(dec_str).map_err(|_| "Could not convert string to integer!")?;
fraction_digits = u64::from_str(backtrace).unwrap_or(0);
} else {
integer_digits = absolute_value as u64;
num_fraction_digits0 = 0;
num_fraction_digits = 0;
fraction_digits0 = 0;
fraction_digits = 0;
}
Ok(PluralOperands {
n: absolute_value,
i: integer_digits,
v: num_fraction_digits0,
w: num_fraction_digits,
f: fraction_digits0,
t: fraction_digits,
})
}
}
macro_rules! impl_integer_type {
($ty:ident) => {
impl From<$ty> for PluralOperands {
fn from(input: $ty) -> Self {
// XXXManishearth converting from u32 or u64 to isize may wrap
PluralOperands {
n: input as f64,
i: input as u64,
v: 0,
w: 0,
f: 0,
t: 0,
}
}
}
};
($($ty:ident)+) => {
$(impl_integer_type!($ty);)+
};
}
macro_rules! impl_signed_integer_type {
($ty:ident) => {
impl TryFrom<$ty> for PluralOperands {
type Error = &'static str;
fn try_from(input: $ty) -> Result<Self, Self::Error> {
// XXXManishearth converting from i64 to isize may wrap
let x = (input as isize).checked_abs().ok_or("Number too big")?;
Ok(PluralOperands {
n: x as f64,
i: x as u64,
v: 0,
w: 0,
f: 0,
t: 0,
})
}
}
};
($($ty:ident)+) => {
$(impl_signed_integer_type!($ty);)+
};
}
macro_rules! impl_convert_type {
($ty:ident) => {
impl TryFrom<$ty> for PluralOperands {
type Error = &'static str;
fn try_from(input: $ty) -> Result<Self, Self::Error> {
let as_str: &str = &input.to_string();
PluralOperands::try_from(as_str)
}
}
};
($($ty:ident)+) => {
$(impl_convert_type!($ty);)+
};
}
impl_integer_type!(u8 u16 u32 u64 usize);
impl_signed_integer_type!(i8 i16 i32 i64 isize);
// XXXManishearth we can likely have dedicated float impls here
impl_convert_type!(f32 f64 String);
File diff suppressed because it is too large Load Diff