rust_icu_uformattable/
lib.rs

1// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! # ICU number formatting support for rust
16
17use {
18    rust_icu_common as common, rust_icu_sys as sys,
19    rust_icu_sys::versioned_function,
20    rust_icu_ustring as ustring,
21    std::{convert::TryFrom, ffi, os::raw, ptr},
22};
23
24// Implements the ICU type [`UFormattable`][ufmt].
25//
26// [UFormattable] is a thin wrapper for primitive types used for number formatting.
27//
28// Note from the ICU4C API:
29//
30// > Underlying is a C interface to the class `icu::Formatable`.  Static functions
31// on this class convert to and from this interface (via `reinterpret_cast`). Many
32// operations are not thread safe, and should not be shared between threads.
33//
34//   [ufmt]: https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/uformattable_8h.html
35#[derive(Debug)]
36pub struct UFormattable<'a> {
37    // The underlying representation.
38    rep: ptr::NonNull<sys::UFormattable>,
39    owner: Option<&'a Self>,
40}
41
42impl<'a> Drop for crate::UFormattable<'a> {
43    /// Implements `ufmt_close`.
44    fn drop(&mut self) {
45        if let None = self.owner {
46            unsafe { versioned_function!(ufmt_close)(self.rep.as_ptr()) };
47        }
48    }
49}
50
51/// Generates a simple getter, which just shunts into an appropriately
52/// named getter function of the sys crate and returns the appropriate type.
53///
54/// Example:
55///
56/// ```ignore
57/// simple_getter!(get_array_length, ufmt_getArrayLength, i32);
58/// ```
59///
60/// * `$method_name` is an identifier
61macro_rules! simple_getter {
62    ($method_name:ident, $impl_function_name:ident, $return_type:ty) => {
63        #[doc = concat!("Implements `", stringify!($impl_function_name), "`.")]
64        ///
65        /// Use [UFormattable::get_type] to verify that the type matches.
66        pub fn $method_name(&self) -> Result<$return_type, common::Error> {
67            let mut status = common::Error::OK_CODE;
68            let ret = unsafe {
69                assert!(common::Error::is_ok(status));
70                versioned_function!($impl_function_name)(self.rep.as_ptr(), &mut status)
71            };
72            common::Error::ok_or_warning(status)?;
73            Ok(ret)
74        }
75    };
76}
77
78impl<'a> crate::UFormattable<'a> {
79    /// Initialize a [crate::UFormattable] to type `UNUM_LONG`, value 0 may
80    /// return error.
81    ///
82    /// Implements `ufmt_open`.
83    pub fn try_new<'b>() -> Result<crate::UFormattable<'b>, common::Error> {
84        let mut status = common::Error::OK_CODE;
85        // We verify that status is OK on entry and on exit, and that the
86        // returned representation is not null.
87        let rep = unsafe {
88            assert!(common::Error::is_ok(status));
89            versioned_function!(ufmt_open)(&mut status)
90        };
91        common::Error::ok_or_warning(status)?;
92        Ok(UFormattable {
93            rep: ptr::NonNull::new(rep).unwrap(),
94            owner: None,
95        })
96    }
97
98    /// Reveals the underlying representation as a mutable pointer.
99    ///
100    /// **DO NOT USE UNLESS YOU HAVE NO OTHER CHOICE**
101    ///
102    /// The intended use of this method is for other crates that need to obtain
103    /// low-level representations of this type.
104    #[doc(hidden)]
105    pub fn as_mut_ptr(&mut self) -> *mut sys::UFormattable {
106        self.rep.as_ptr()
107    }
108
109    pub fn as_ptr(&self) -> *const sys::UFormattable {
110        self.rep.as_ptr()
111    }
112
113    /// Returns `true` if this formattable is numeric.
114    ///
115    /// Implements `ufmt_isNumeric`
116    pub fn is_numeric(&self) -> bool {
117        let ubool = unsafe { versioned_function!(ufmt_isNumeric)(self.rep.as_ptr()) };
118        match ubool {
119            0i8 => false,
120            _ => true,
121        }
122    }
123
124    // Returns the type of this formattable.  The comment here and below is
125    // used in coverage analysis; the macro `simple_getter!` generates
126    // user-visible documentation.
127    //
128    // Implements `ufmt_getType`
129    simple_getter!(get_type, ufmt_getType, sys::UFormattableType);
130
131    // Implements `ufmt_getDate`
132    simple_getter!(get_date, ufmt_getDate, sys::UDate);
133
134    // Implements `ufmt_getDouble`
135    simple_getter!(get_double, ufmt_getDouble, f64);
136
137    // Implements `ufmt_getLong`
138    simple_getter!(get_i32, ufmt_getLong, i32);
139
140    // Implements `ufmt_getInt64`
141    simple_getter!(get_i64, ufmt_getInt64, i64);
142
143    // Implements `ufmt_getArrayLength`
144    simple_getter!(get_array_length, ufmt_getArrayLength, i32);
145
146    // Implements `ufmt_getUChars`
147    pub fn get_ustring(&self) -> Result<ustring::UChar, common::Error> {
148        let mut status = common::Error::OK_CODE;
149        let mut ustrlen = 0i32;
150        let raw = unsafe {
151            assert!(common::Error::is_ok(status));
152            versioned_function!(ufmt_getUChars)(self.rep.as_ptr(), &mut ustrlen, &mut status)
153        } as *mut sys::UChar;
154        common::Error::ok_or_warning(status)?;
155        let ret = unsafe {
156            assert_ne!(raw, 0 as *mut sys::UChar);
157            assert!(ustrlen >= 0);
158            ustring::UChar::clone_from_raw_parts(raw, ustrlen)
159        };
160        Ok(ret)
161    }
162
163    /// Implements `ufmt_getUChars`
164    pub fn get_str(&self) -> Result<String, common::Error> {
165        let ustr = self.get_ustring()?;
166        String::try_from(&ustr)
167    }
168
169    /// Use [UFormattable::get_type] to ensure that this formattable is an array before using this
170    /// method.  Otherwise you will get an error.  The lifetime of the resulting formattable is tied
171    /// to this one.
172    ///
173    /// Implements `ufmt_getArrayItemByIndex`
174    pub fn get_array_item_by_index(
175        &'a self,
176        index: i32,
177    ) -> Result<crate::UFormattable<'a>, common::Error> {
178        let mut status = common::Error::OK_CODE;
179        let raw: *mut sys::UFormattable = unsafe {
180            assert!(common::Error::is_ok(status));
181            versioned_function!(ufmt_getArrayItemByIndex)(self.rep.as_ptr(), index, &mut status)
182        };
183        common::Error::ok_or_warning(status)?;
184        assert_ne!(raw, 0 as *mut sys::UFormattable);
185        Ok(UFormattable {
186            rep: ptr::NonNull::new(raw).unwrap(),
187            owner: Some(&self),
188        })
189    }
190
191    /// Implements `ufmt_getDecNumChars`
192    pub fn get_dec_num_chars(&self) -> Result<String, common::Error> {
193        let mut status = common::Error::OK_CODE;
194        let mut cstrlen = 0i32;
195        let raw: *const raw::c_char = unsafe {
196            assert!(common::Error::is_ok(status));
197            versioned_function!(ufmt_getDecNumChars)(self.rep.as_ptr(), &mut cstrlen, &mut status)
198        };
199        common::Error::ok_or_warning(status)?;
200        let ret = unsafe {
201            assert_ne!(raw, 0 as *const raw::c_char);
202            assert!(cstrlen >= 0);
203            ffi::CStr::from_ptr(raw)
204        };
205        Ok(ret
206            .to_str()
207            .map_err(|e: std::str::Utf8Error| common::Error::from(e))?
208            .to_string())
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use crate::*;
215
216    // There doesn't seem to be a way to initialize a nonzero Numeric for testing all of this code.
217    // So it seems it would have to remain uncovered with tests, until some other code gets to
218    // use it.
219
220    #[test]
221    fn basic() {
222        let n = crate::UFormattable::try_new().expect("try_new");
223        assert_eq!(
224            sys::UFormattableType::UFMT_LONG,
225            n.get_type().expect("get_type")
226        );
227        assert_eq!(0, n.get_i32().expect("get_i32"));
228        assert_eq!("0", n.get_dec_num_chars().expect("get_dec_num_chars"));
229    }
230}