From 263908bfbd6e755067147b417908e7b4f9112b63 Mon Sep 17 00:00:00 2001 From: Filip Filmar Date: Wed, 24 Jun 2020 15:06:52 -0700 Subject: [PATCH] Continuing implementation of unum.h See issue #141. --- rust_icu_unum/src/lib.rs | 145 +++++++++++++++++++++++++----------- rust_icu_ustring/src/lib.rs | 2 +- 2 files changed, 103 insertions(+), 44 deletions(-) diff --git a/rust_icu_unum/src/lib.rs b/rust_icu_unum/src/lib.rs index 4fa31e74..f772e586 100644 --- a/rust_icu_unum/src/lib.rs +++ b/rust_icu_unum/src/lib.rs @@ -17,10 +17,11 @@ //! Since 0.3.1 use { - rust_icu_common as common, rust_icu_sys as sys, + 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_ustring::buffered_uchar_method_with_retry, std::{convert::TryFrom, convert::TryInto, ptr}, }; @@ -36,6 +37,43 @@ impl Drop for UNumberFormat { } } +/// There is a slew of near-identical method calls which differ in the type of +/// the input argument and the name of the function to invoke. +macro_rules! format_ustring_for_type{ + ($method_name:ident, $function_name:ident, $type_decl:ty) => ( + /// Implements `$function_name`. + pub fn $method_name(&self, number: $type_decl) -> Result { + let result = paste::item! { + self. [< $method_name _ustring>] (number)? + }; + String::try_from(&result) + } + + // Should be able to use https://github.com/google/rust_icu/pull/144 to + // make this even shorter. + paste::item! { + /// Implements `$function_name`. + pub fn [<$method_name _ustring>] (&self, param: $type_decl) -> Result { + const CAPACITY: usize = 200; + buffered_uchar_method_with_retry!( + [< $method_name _ustring_impl >], + CAPACITY, + [ rep: *const sys::UNumberFormat, param: $type_decl, ], + [ field: *mut sys::UFieldPosition, ] + ); + + [<$method_name _ustring_impl>]( + versioned_function!($function_name), + self.rep.as_ptr(), + param, + // The field position is unused for now. + 0 as *mut sys::UFieldPosition, + ) + } + } + ) +} + impl UNumberFormat { /// Implements `unum_open`, with a pattern. pub fn try_new_decimal_pattern_ustring( @@ -117,56 +155,77 @@ impl UNumberFormat { }) } - /// Implements `unum_format` - pub fn format(&self, number: i32) -> Result { - let result = self.format_ustring(number)?; - String::try_from(&result) + // Can we make this into a generic method somehow? + + // Implements `unum_format` + format_ustring_for_type!(format, unum_format, i32); + + // Implements `unum_formatInt64` + format_ustring_for_type!(format_i64, unum_formatInt64, i64); + + // Implements `unum_formatDouble` + format_ustring_for_type!(format_f64, unum_formatDouble, f64); +} + +/// Used to iterate over the field positions. +pub struct UFieldPositionIterator<'a, T> { + rep: ptr::NonNull, + // Owner does not own the representation above, but does own the underlying + // data. + owner: Option<&'a T>, +} + +impl<'a, T> Drop for UFieldPositionIterator<'a, T> { + fn drop(&mut self) { + unsafe { versioned_function!(ufieldpositer_close)(self.rep.as_ptr()) }; } +} - /// Implements `unum_format` - // TODO: this method call is repetitive, and should probably be pulled out into a macro. - pub fn format_ustring(&self, number: i32) -> Result { - const CAPACITY: usize = 200; +impl<'a, T> UFieldPositionIterator<'a, T> { + pub fn try_new_owned(owner: &'a T) -> Result, common::Error> { let mut status = common::Error::OK_CODE; - let mut buf: Vec = vec![0; CAPACITY]; + let raw = unsafe { + assert!(common::Error::is_ok(status)); + versioned_function!(ufieldpositer_open)(&mut status) + }; + common::Error::ok_or_warning(status)?; + Ok(UFieldPositionIterator { + rep: ptr::NonNull::new(raw).expect("raw pointer is not null"), + owner: None, + }) + } - let full_len: i32 = unsafe { + pub fn try_new_unowned<'b>() -> Result, common::Error> { + let mut status = common::Error::OK_CODE; + let raw = unsafe { assert!(common::Error::is_ok(status)); - versioned_function!(unum_format)( - self.rep.as_ptr(), - number, - buf.as_mut_ptr(), - buf.len() as i32, - // Unsure what this field should be for. - 0 as *mut sys::UFieldPosition, - &mut status, - ) + versioned_function!(ufieldpositer_open)(&mut status) }; - if status == sys::UErrorCode::U_BUFFER_OVERFLOW_ERROR - || (common::Error::is_ok(status) - && full_len > CAPACITY.try_into().map_err(|e| common::Error::wrapper(e))?) - { - assert!(full_len > 0); - let full_len: usize = full_len.try_into().map_err(|e| common::Error::wrapper(e))?; - buf.resize(full_len, 0); - unsafe { - assert!(common::Error::is_ok(status)); - versioned_function!(unum_format)( - self.rep.as_ptr(), - number, - buf.as_mut_ptr(), - buf.len() as i32, - 0 as *mut sys::UFieldPosition, - &mut status, - ) - }; - } common::Error::ok_or_warning(status)?; - if full_len >= 0 { - let full_len: usize = full_len.try_into().map_err(|e| common::Error::wrapper(e))?; - buf.resize(full_len, 0); + assert_ne!(raw, 0 as *mut sys::UFieldPositionIterator); + Ok(UFieldPositionIterator { + rep: ptr::NonNull::new(raw).expect("raw pointer is not null"), + owner: None, + }) + } +} + +impl<'a, T> Iterator for UFieldPositionIterator<'a, T> { + // TODO: Consider turning this into a range once the range properties + // are known. + /// The begin of the range and the end of the range index, in that order. + type Item = (i32, i32); + + /// Gets the next position iterator pair. + fn next(&mut self) -> Option { + let mut begin = 0i32; + let mut end = 0i32; + + unsafe { versioned_function!(fieldpositer_next)(self.rep.as_ptr(), &mut begin, &mut end) }; + if begin < 0 || end < 0 { + return None; } - Ok(ustring::UChar::from(buf)) + Some((begin, end)) } } diff --git a/rust_icu_ustring/src/lib.rs b/rust_icu_ustring/src/lib.rs index 49a802e0..5a62ac3a 100644 --- a/rust_icu_ustring/src/lib.rs +++ b/rust_icu_ustring/src/lib.rs @@ -38,7 +38,7 @@ pub struct UChar { rep: Vec, } -/// Same as [common::buffered_string_method_with_retry], but for unicode strings. +/// Same as `rust_icu_common::buffered_string_method_with_retry`, but for unicode strings. /// /// Example use: ///