Skip to content

Commit

Permalink
Adds support for UFormattable.
Browse files Browse the repository at this point in the history
Using uformattable::UFormattable provides
generalized value parsing for the particular
locale.

While this isn't the full spectrum of the
underlying functions that ICU4C provides
for value parsing, it's general enough
to be usable for all the use cases where
parsing is needed, *if* needed.  However,
those uses are rare.

Issue google#141.
  • Loading branch information
filmil committed Jun 26, 2020
1 parent 1efdf34 commit ca44d7b
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 9 deletions.
15 changes: 15 additions & 0 deletions rust_icu_uformattable/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,21 @@ impl<'a> crate::UFormattable<'a> {
})
}

/// Reveals the underlying representation as a mutable pointer.
///
/// **DO NOT USE UNLESS YOU HAVE NO OTHER CHOICE**
///
/// The intended use of this method is for other crates that need to obtain
/// low-level representations of this type.
#[doc(hidden)]
pub fn as_mut_ptr(&mut self) -> *mut sys::UFormattable {
self.rep.as_ptr()
}

pub fn as_ptr(&self) -> *const sys::UFormattable {
self.rep.as_ptr()
}

/// Returns `true` if this formattable is numeric.
///
/// Implements `ufmt_isNumeric`
Expand Down
7 changes: 7 additions & 0 deletions rust_icu_unum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ log = "0.4.6"
paste = "0.1.5"
rust_icu_common = { path = "../rust_icu_common", version = "0.3.1", default-features = false }
rust_icu_sys = { path = "../rust_icu_sys", version = "0.3.1", default-features = false }
rust_icu_uformattable = { path = "../rust_icu_uformattable", version = "0.3.1", default-features = false }
rust_icu_uloc = { path = "../rust_icu_uloc", version = "0.3.1", default-features = false }
rust_icu_ustring = { path = "../rust_icu_ustring", version = "0.3.1", default-features = false }
anyhow = "1.0.25"
Expand All @@ -34,36 +35,42 @@ default = ["use-bindgen", "renaming", "icu_config"]
use-bindgen = [
"rust_icu_common/use-bindgen",
"rust_icu_sys/use-bindgen",
"rust_icu_uformattable/use-bindgen",
"rust_icu_uloc/use-bindgen",
"rust_icu_ustring/use-bindgen",
]
renaming = [
"rust_icu_common/renaming",
"rust_icu_sys/renaming",
"rust_icu_uformattable/renaming",
"rust_icu_uloc/renaming",
"rust_icu_ustring/renaming",
]
icu_config = [
"rust_icu_common/icu_config",
"rust_icu_sys/icu_config",
"rust_icu_uformattable/icu_config",
"rust_icu_uloc/icu_config",
"rust_icu_ustring/icu_config",
]
icu_version_in_env = [
"rust_icu_common/icu_version_in_env",
"rust_icu_sys/icu_version_in_env",
"rust_icu_uformattable/icu_version_in_env",
"rust_icu_uloc/icu_version_in_env",
"rust_icu_ustring/icu_version_in_env",
]
icu_version_64_plus = [
"rust_icu_common/icu_version_64_plus",
"rust_icu_sys/icu_version_64_plus",
"rust_icu_uformattable/icu_version_64_plus",
"rust_icu_uloc/icu_version_64_plus",
"rust_icu_ustring/icu_version_64_plus",
]
icu_version_67_plus = [
"rust_icu_common/icu_version_67_plus",
"rust_icu_sys/icu_version_67_plus",
"rust_icu_uformattable/icu_version_67_plus",
"rust_icu_uloc/icu_version_67_plus",
"rust_icu_ustring/icu_version_67_plus",
]
Expand Down
150 changes: 141 additions & 9 deletions rust_icu_unum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use {
paste, rust_icu_common as common, rust_icu_sys as sys,
rust_icu_sys::versioned_function,
rust_icu_sys::*,
rust_icu_uloc as uloc, rust_icu_ustring as ustring,
rust_icu_uformattable as uformattable, rust_icu_uloc as uloc, rust_icu_ustring as ustring,
rust_icu_ustring::buffered_uchar_method_with_retry,
std::{convert::TryFrom, convert::TryInto, ptr},
};
Expand Down Expand Up @@ -235,21 +235,29 @@ impl UNumberFormat {
}

/// Implements `unum_formatDoubleCurrency`. Since 0.3.1.
pub fn format_double_currency(&self, number: f64, currency: &str) -> Result<String, common::Error> {
pub fn format_double_currency(
&self,
number: f64,
currency: &str,
) -> Result<String, common::Error> {
let currency = ustring::UChar::try_from(currency)?;
let result = self.format_double_currency_ustring(number, &currency)?;
String::try_from(&result)
}

/// Implements `unum_formatDoubleCurrency`. Since 0.3.1
pub fn format_double_currency_ustring(&self, number: f64, currency: &ustring::UChar) -> Result<ustring::UChar, common::Error> {
pub fn format_double_currency_ustring(
&self,
number: f64,
currency: &ustring::UChar,
) -> Result<ustring::UChar, common::Error> {
const CAPACITY: usize = 200;
buffered_uchar_method_with_retry!(
format_double_currency_impl,
CAPACITY,
[
format: *const sys::UNumberFormat,
number: f64,
number: f64,
// NUL terminated!
currency: *mut sys::UChar,
],
Expand All @@ -263,12 +271,83 @@ impl UNumberFormat {
format_double_currency_impl(
versioned_function!(unum_formatDoubleCurrency),
self.rep.as_ptr(),
number,
number,
currencyz.as_mut_c_ptr(),
0 as *mut sys::UFieldPosition,
)
}


/// Implements `unum_parseToUFormattable`. Since 0.3.1.
///
/// > **WARNING** the `parse_position` parameter is with respect to the number index
/// in the `UChar` string. This won't work exactly for multibyte UTF8 values of
/// `text`. If you think you will have multibyte values, use instead
/// [UNumberFormat::parse_to_formattable_ustring].
pub fn parse_to_formattable<'a>(
&'a self,
text: &str,
parse_position: Option<i32>,
) -> Result<uformattable::UFormattable<'a>, common::Error> {
let ustr = ustring::UChar::try_from(text)?;
self.parse_to_formattable_ustring(&ustr, parse_position)
}

/// Implements `unum_parseToUFormattable`. Since 0.3.1.
pub fn parse_to_formattable_ustring<'a>(
&'a self,
text: &ustring::UChar,
parse_position: Option<i32>,
) -> Result<uformattable::UFormattable<'a>, common::Error> {
let mut fmt = uformattable::UFormattable::try_new()?;
let mut status = common::Error::OK_CODE;
let mut pos = parse_position.unwrap_or(0);
unsafe {
assert!(common::Error::is_ok(status));
versioned_function!(unum_parseToUFormattable)(
self.rep.as_ptr(),
fmt.as_mut_ptr(),
text.as_c_ptr(),
text.len() as i32,
&mut pos,
&mut status,
)
};
common::Error::ok_or_warning(status)?;
Ok(fmt)
}

/// Implements `unum_formatUFormattable`. Since 0.3.1.
pub fn format_formattable<'a>(
&self,
fmt: &uformattable::UFormattable<'a>,
) -> Result<String, common::Error> {
let result = self.format_formattable_ustring(fmt)?;
String::try_from(&result)
}

/// Implements `unum_formatUFormattable`. Since 0.3.1.
pub fn format_formattable_ustring<'a>(
&self,
fmt: &uformattable::UFormattable<'a>,
) -> Result<ustring::UChar, common::Error> {
const CAPACITY: usize = 200;
buffered_uchar_method_with_retry!(
format_formattable_impl,
CAPACITY,
[
format: *const sys::UNumberFormat,
fmt: *const sys::UFormattable,
],
[pos: *mut sys::UFieldPosition,]
);

format_formattable_impl(
versioned_function!(unum_formatUFormattable),
self.rep.as_ptr(),
fmt.as_ptr(),
0 as *mut sys::UFieldPosition,
)
}
}

/// Used to iterate over the field positions.
Expand Down Expand Up @@ -536,9 +615,7 @@ mod tests {
let fmt =
crate::UNumberFormat::try_new_with_style(test.style, &locale).expect("formatter");

let s = fmt
.format_decimal(test.number)
.expect("format success");
let s = fmt.format_decimal(test.number).expect("format success");

assert_eq!(test.expected, s);
}
Expand Down Expand Up @@ -573,4 +650,59 @@ mod tests {
assert_eq!(test.expected, s);
}
}

#[test]
fn format_and_parse_uformattable() {
#[derive(Debug)]
struct TestCase {
source_locale: &'static str,
number: &'static str,
position: Option<i32>,
style: sys::UNumberFormatStyle,

target_locale: &'static str,
expected: &'static str,
};

let tests = vec![
TestCase {
source_locale: "sr-RS",
number: "123,44",
position: None,
style: sys::UNumberFormatStyle::UNUM_DECIMAL,

target_locale: "en-US",
expected: "123.44",
},

TestCase {
source_locale: "sr-RS",
number: "123,44",
position: Some(2),
style: sys::UNumberFormatStyle::UNUM_DECIMAL,

target_locale: "en-US",
expected: "3.44",
},
];
for test in tests {
let locale = uloc::ULoc::try_from(test.source_locale).expect("locale exists");
let fmt =
crate::UNumberFormat::try_new_with_style(test.style, &locale).expect("source_locale formatter");

let formattable = fmt
.parse_to_formattable(test.number, test.position)
.expect(&format!("parse_to_formattable: {:?}", &test));

let locale = uloc::ULoc::try_from(test.target_locale).expect("locale exists");
let fmt =
crate::UNumberFormat::try_new_with_style(test.style, &locale).expect("target_locale formatter");

let result = fmt
.format_formattable(&formattable)
.expect(&format!("format_formattable: {:?}", &test));

assert_eq!(test.expected, result);
}
}
}

0 comments on commit ca44d7b

Please sign in to comment.