Skip to content

Commit

Permalink
Adding Percent Formatter to Experimental (#5255)
Browse files Browse the repository at this point in the history
  • Loading branch information
blaynem authored Jul 27, 2024
1 parent 724131e commit 46aace9
Show file tree
Hide file tree
Showing 30 changed files with 1,119 additions and 313 deletions.
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ See the [Testing](#testing) section below for more information on the various te
There are various files that auto-generated across the ICU4X repository. Here are some of the commands that you may
need to run in order to recreate them. These files may be run in more comprehensive tests such as those included in `cargo make ci-job-test` or `cargo make ci-all`.

- `cargo make testdata` - regenerates all test data in the `provider/testdata` directory.
- `cargo make testdata` - regenerates all test data in the `provider/source/debug` directory.
- `cargo make bakeddata` - regenerates baked data in the `provider/data` directory.
- `cargo make bakeddata foo` can be used to generate data in `provider/data/foo` only.
- `cargo make generate-readmes` - generates README files according to Rust docs. Output files must be committed in git for check to pass.
- `cargo make diplomat-gen` - recreates the Diplomat generated files in the `ffi/capi` directory.

Expand Down
6 changes: 6 additions & 0 deletions components/decimal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ pub struct FixedDecimalFormatter {
symbols: DataPayload<provider::DecimalSymbolsV1Marker>,
}

impl AsRef<FixedDecimalFormatter> for FixedDecimalFormatter {
fn as_ref(&self) -> &FixedDecimalFormatter {
self
}
}

impl FixedDecimalFormatter {
icu_provider::gen_any_buffer_data_constructors!(

Expand Down
1 change: 1 addition & 0 deletions components/experimental/src/dimension/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
//! Experimental.

pub mod currency;
pub mod percent;
pub mod provider;
pub mod units;
222 changes: 222 additions & 0 deletions components/experimental/src/dimension/percent/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
// 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 fixed_decimal::{FixedDecimal, Sign};
use icu_decimal::FixedDecimalFormatter;

use crate::alloc::borrow::ToOwned;
use alloc::borrow::Cow;
use writeable::Writeable;

use crate::dimension::provider::percent::PercentEssentialsV1;

use super::options::{Display, PercentFormatterOptions};

struct Append<W1, W2>(W1, W2);
// This allows us to combines two writeables together.
impl<W1: Writeable, W2: Writeable> Writeable for Append<W1, W2> {
fn write_to<W>(&self, sink: &mut W) -> core::result::Result<(), core::fmt::Error>
where
W: core::fmt::Write + ?Sized,
{
self.0.write_to(sink)?;
self.1.write_to(sink)
}
}

pub struct FormattedPercent<'l> {
pub(crate) value: &'l FixedDecimal,
pub(crate) essential: &'l PercentEssentialsV1<'l>,
pub(crate) options: &'l PercentFormatterOptions,
pub(crate) fixed_decimal_formatter: &'l FixedDecimalFormatter,
}

impl<'l> Writeable for FormattedPercent<'l> {
fn write_to<W>(&self, sink: &mut W) -> core::result::Result<(), core::fmt::Error>
where
W: core::fmt::Write + ?Sized,
{
// Removing the sign from the value
let abs_value = match self.value.sign() {
Sign::Negative => self.value.clone().with_sign(Sign::None),
_ => self.value.to_owned(),
};

let value = self.fixed_decimal_formatter.format(&abs_value);

match self.options.display {
// In the Standard display, we take the unsigned pattern only when the value is positive.
Display::Standard => {
if self.value.sign() == Sign::Negative {
self.essential
.signed_pattern
.interpolate((value, &self.essential.minus_sign))
.write_to(sink)?
} else {
self.essential
.unsigned_pattern
.interpolate([value])
.write_to(sink)?
};
}
Display::Approximate => {
let sign = if self.value.sign() == Sign::Negative {
// The approximate sign gets pre-pended
Append(
&self.essential.approximately_sign,
&self.essential.minus_sign,
)
} else {
Append(&self.essential.approximately_sign, &Cow::Borrowed(""))
};

self.essential
.signed_pattern
.interpolate((value, sign))
.write_to(sink)?;
}
Display::ExplicitSign => self
.essential
.signed_pattern
.interpolate((
value,
if self.value.sign() == Sign::Negative {
&self.essential.minus_sign
} else {
&self.essential.plus_sign
},
))
.write_to(sink)?,
};

Ok(())
}
}

writeable::impl_display_with_writeable!(FormattedPercent<'_>);

#[cfg(test)]
mod tests {
use icu_locale_core::locale;
use writeable::assert_writeable_eq;

use crate::dimension::percent::{
formatter::PercentFormatter,
options::{Display, PercentFormatterOptions},
};

#[test]
pub fn test_en_us() {
let locale = locale!("en-US").into();
// Positive case
let positive_value = "12345.67".parse().unwrap();
let default_fmt = PercentFormatter::try_new(&locale, Default::default()).unwrap();
let formatted_percent = default_fmt.format(&positive_value);
assert_writeable_eq!(formatted_percent, "12,345.67%");

// Negative case
let neg_value = "-12345.67".parse().unwrap();
let formatted_percent = default_fmt.format(&neg_value);
assert_writeable_eq!(formatted_percent, "-12,345.67%");

// Approximate Case
let approx_value = "12345.67".parse().unwrap();
let approx_fmt = PercentFormatter::try_new(
&locale,
PercentFormatterOptions {
display: Display::Approximate,
},
)
.unwrap();
let formatted_percent = approx_fmt.format(&approx_value);
assert_writeable_eq!(formatted_percent, "~12,345.67%");

// ExplicitSign Case
let explicit_fmt = PercentFormatter::try_new(
&locale,
PercentFormatterOptions {
display: Display::ExplicitSign,
},
)
.unwrap();
let formatted_percent = explicit_fmt.format(&positive_value);
assert_writeable_eq!(formatted_percent, "+12,345.67%");
}

#[test]
pub fn test_tr() {
let locale = locale!("tr").into();
// Positive case
let positive_value = "12345.67".parse().unwrap();
let default_fmt = PercentFormatter::try_new(&locale, Default::default()).unwrap();
let formatted_percent = default_fmt.format(&positive_value);
assert_writeable_eq!(formatted_percent, "%12.345,67");

// Negative case
let neg_value = "-12345.67".parse().unwrap();
let formatted_percent = default_fmt.format(&neg_value);
assert_writeable_eq!(formatted_percent, "-%12.345,67");

// Approximate Case
let approx_value = "12345.67".parse().unwrap();
let approx_fmt = PercentFormatter::try_new(
&locale,
PercentFormatterOptions {
display: Display::Approximate,
},
)
.unwrap();
let formatted_percent = approx_fmt.format(&approx_value);
assert_writeable_eq!(formatted_percent, "~%12.345,67");

// ExplicitSign Case
let explicit_fmt = PercentFormatter::try_new(
&locale,
PercentFormatterOptions {
display: Display::ExplicitSign,
},
)
.unwrap();
let formatted_percent = explicit_fmt.format(&positive_value);
assert_writeable_eq!(formatted_percent, "+%12.345,67");
}

#[test]
pub fn test_blo() {
let locale = locale!("blo").into();
// Positive case
let positive_value = "12345.67".parse().unwrap();
let default_fmt = PercentFormatter::try_new(&locale, Default::default()).unwrap();
let formatted_percent = default_fmt.format(&positive_value);
assert_writeable_eq!(formatted_percent, "%\u{a0}12\u{a0}345,67");

// Negative case
let neg_value = "-12345.67".parse().unwrap();
let formatted_percent = default_fmt.format(&neg_value);
assert_writeable_eq!(formatted_percent, "%\u{a0}-12\u{a0}345,67");

// Approximate Case
let approx_value = "12345.67".parse().unwrap();
let approx_fmt = PercentFormatter::try_new(
&locale,
PercentFormatterOptions {
display: Display::Approximate,
},
)
.unwrap();
let formatted_percent = approx_fmt.format(&approx_value);
assert_writeable_eq!(formatted_percent, "%\u{a0}~12\u{a0}345,67");

// ExplicitSign Case
let explicit_fmt = PercentFormatter::try_new(
&locale,
PercentFormatterOptions {
display: Display::ExplicitSign,
},
)
.unwrap();
let formatted_percent = explicit_fmt.format(&positive_value);
assert_writeable_eq!(formatted_percent, "%\u{a0}+12\u{a0}345,67");
}
}
Loading

0 comments on commit 46aace9

Please sign in to comment.