#![feature(proc_macro_hygiene)]
use {
paste, rust_icu_common as common,
rust_icu_common::simple_drop_impl,
rust_icu_sys as sys,
rust_icu_sys::versioned_function,
rust_icu_sys::*,
rust_icu_uloc as uloc, rust_icu_unum as unum, rust_icu_ustring as ustring,
rust_icu_ustring::buffered_uchar_method_with_retry,
std::{convert::TryFrom, convert::TryInto, ptr},
};
macro_rules! format_type {
($method_name:ident, $impl_function_name:ident, $value_type:ty) => {
pub fn $method_name(&self, value: $value_type) -> Result<UFormattedNumber, common::Error> {
let mut result = UFormattedNumber::try_new()?;
let mut status = sys::UErrorCode::U_ZERO_ERROR;
unsafe {
versioned_function!($impl_function_name)(
self.rep.as_ptr(),
value,
result.as_c_mut_ptr(),
&mut status,
)
};
common::Error::ok_or_warning(status)?;
Ok(result)
}
};
}
#[derive(Debug)]
pub struct UNumberFormatter {
rep: ptr::NonNull<sys::UNumberFormatter>,
}
simple_drop_impl!(UNumberFormatter, unumf_close);
impl UNumberFormatter {
pub fn try_new_ustring(
skeleton: &ustring::UChar,
locale: &uloc::ULoc,
) -> Result<UNumberFormatter, common::Error> {
let mut status = sys::UErrorCode::U_ZERO_ERROR;
#[cfg(feature = "icu_version_64_plus")]
{
let mut parse_status = common::NO_PARSE_ERROR.clone();
let rep = unsafe {
assert!(common::Error::is_ok(status));
versioned_function!(unumf_openForSkeletonAndLocaleWithError)(
skeleton.as_c_ptr(),
skeleton.len() as i32,
locale.label().as_ptr() as *const std::os::raw::c_char,
&mut parse_status,
&mut status,
)
};
assert_ne!(rep, 0 as *mut sys::UNumberFormatter);
common::parse_ok(parse_status)?;
common::Error::ok_or_warning(status)?;
let result = UNumberFormatter {
rep: std::ptr::NonNull::new(rep).unwrap(),
};
return Ok(result);
}
#[cfg(not(feature = "icu_version_64_plus"))]
{
let rep = unsafe {
assert!(common::Error::is_ok(status));
versioned_function!(unumf_openForSkeletonAndLocale)(
skeleton.as_c_ptr(),
skeleton.len() as i32,
locale.label().as_ptr() as *const std::os::raw::c_char,
&mut status,
)
};
assert_ne!(rep, 0 as *mut sys::UNumberFormatter);
common::Error::ok_or_warning(status)?;
let result = UNumberFormatter {
rep: std::ptr::NonNull::new(rep).unwrap(),
};
return Ok(result);
}
}
pub fn try_new(skeleton: &str, locale: &str) -> Result<UNumberFormatter, common::Error> {
let locale = uloc::ULoc::try_from(locale)?;
let skeleton = ustring::UChar::try_from(skeleton)?;
UNumberFormatter::try_new_ustring(&skeleton, &locale)
}
format_type!(format_int, unumf_formatInt, i64);
format_type!(format_double, unumf_formatDouble, f64);
pub fn format_decimal(&self, value: &str) -> Result<UFormattedNumber, common::Error> {
let mut status = sys::UErrorCode::U_ZERO_ERROR;
let mut result = UFormattedNumber::try_new()?;
unsafe {
versioned_function!(unumf_formatDecimal)(
self.rep.as_ptr(),
value.as_ptr() as *const std::os::raw::c_char,
value.len() as i32,
result.as_c_mut_ptr(),
&mut status,
)
};
common::Error::ok_or_warning(status)?;
Ok(result)
}
}
#[derive(Debug)]
pub struct UFormattedNumber {
rep: std::ptr::NonNull<sys::UFormattedNumber>,
}
impl UFormattedNumber {
fn try_new() -> Result<Self, common::Error> {
let mut status = sys::UErrorCode::U_ZERO_ERROR;
let rep = unsafe {
assert!(common::Error::is_ok(status));
versioned_function!(unumf_openResult)(&mut status)
};
common::Error::ok_or_warning(status)?;
assert_ne!(rep, 0 as *mut sys::UFormattedNumber);
let result = std::ptr::NonNull::new(rep).unwrap();
Ok(UFormattedNumber { rep: result })
}
fn as_c_mut_ptr(&mut self) -> *mut sys::UFormattedNumber {
self.rep.as_ptr()
}
fn as_c_ptr(&self) -> *const sys::UFormattedNumber {
self.rep.as_ptr()
}
pub fn try_field_iter<'a>(
&'a self,
) -> Result<unum::UFieldPositionIterator<'a, Self>, common::Error> {
let mut result = unum::UFieldPositionIterator::try_new_owned(self)?;
let mut status = sys::UErrorCode::U_ZERO_ERROR;
unsafe {
versioned_function!(unumf_resultGetAllFieldPositions)(
self.as_c_ptr(),
result.as_mut_ptr(),
&mut status,
)
};
common::Error::ok_or_warning(status)?;
Ok(result)
}
}
simple_drop_impl!(UFormattedNumber, unumf_closeResult);
impl TryInto<ustring::UChar> for UFormattedNumber {
type Error = common::Error;
fn try_into(self) -> Result<ustring::UChar, common::Error> {
const CAPACITY: usize = 200;
buffered_uchar_method_with_retry!(
tryinto_impl,
CAPACITY,
[rep: *const sys::UFormattedNumber,],
[]
);
tryinto_impl(versioned_function!(unumf_resultToString), self.rep.as_ptr())
}
}
impl TryInto<String> for UFormattedNumber {
type Error = common::Error;
fn try_into(self) -> Result<String, common::Error> {
let result: ustring::UChar = self.try_into()?;
String::try_from(&result)
}
}
#[cfg(test)]
mod testing {
use std::convert::TryInto;
#[test]
fn basic() {
let fmt = super::UNumberFormatter::try_new(
"measure-unit/length-meter compact-long sign-always",
"sr-RS",
)
.unwrap();
let result = fmt.format_double(123456.7890).unwrap();
let result_str: String = result.try_into().unwrap();
assert_eq!("+123 хиљаде m", result_str);
let result = fmt.format_double(123456.7890).unwrap();
let num_fields = result.try_field_iter().unwrap().count();
assert!(num_fields > 0);
}
#[test]
fn thorough() {
#[derive(Debug, Clone)]
struct TestCase {
locale: &'static str,
number: f64,
skeleton: &'static str,
expected: &'static str,
}
let tests = vec![
TestCase {
locale: "sr-RS",
number: 123456.7890,
skeleton: "measure-unit/length-meter compact-long sign-always",
expected: "+123 хиљаде m",
},
TestCase {
locale: "sr-RS-u-nu-deva",
number: 123456.7890,
skeleton: "measure-unit/length-meter compact-long sign-always",
expected: "+१२३ хиљаде m",
},
TestCase {
locale: "sr-RS",
number: 123456.7890,
skeleton: "numbering-system/deva",
expected: "१२३.४५६,७८९",
},
TestCase {
locale: "en-IN",
number: 123456.7890,
skeleton: "@@@",
expected: "1,23,000",
},
];
for test in tests {
let fmt = super::UNumberFormatter::try_new(&test.skeleton, &test.locale)
.expect(&format!("for test {:?}", &test));
let result = fmt.format_double(test.number).unwrap();
let result_str: String = result.try_into().unwrap();
assert_eq!(test.expected, result_str, "for test {:?}", &test);
}
}
}