rust_icu_utext/
lib.rs

1// Copyright 2019 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
15use {
16    rust_icu_common as common, rust_icu_sys as sys, rust_icu_sys::versioned_function,
17    rust_icu_sys::*, std::cmp::Eq, std::convert::TryFrom, std::os::raw,
18};
19
20#[derive(Debug)]
21pub struct Text {
22    // rep is allocated by the underlying library and has to be dropped using `utext_close`.
23    rep: *mut UText,
24}
25
26impl PartialEq for Text {
27    fn eq(&self, other: &Self) -> bool {
28        let ret = unsafe { versioned_function!(utext_equals)(self.rep, other.rep) };
29        match ret {
30            0 => false,
31            1 => true,
32            // Could be a bug in the underlying library.
33            _ => panic!("value is not convertible to bool in Text::eq: {}", ret),
34        }
35    }
36}
37impl Eq for Text {}
38
39impl TryFrom<String> for Text {
40    type Error = common::Error;
41
42    /// Produces a unicode Text from a rust String.
43    ///
44    /// The conversion may fail if the string is not well formed, and may result in an error.
45    ///
46    /// Implements `utext_open` from ICU4C.
47    fn try_from(s: String) -> Result<Self, Self::Error> {
48        let len: i64 = s.len() as i64;
49        let bytes = s.as_ptr() as *const raw::c_char;
50        // bytes and len must be compatible.  Ensured by the two lines just above.
51        unsafe { Self::from_raw_bytes(bytes, len) }
52    }
53}
54
55impl TryFrom<&str> for Text {
56    type Error = common::Error;
57    /// Implements `utext_open`
58    fn try_from(s: &str) -> Result<Self, Self::Error> {
59        let len = s.len() as i64;
60        let bytes = s.as_ptr() as *const raw::c_char;
61        // bytes and len must be compatible.  Ensured by the two lines just above.
62        unsafe { Self::from_raw_bytes(bytes, len) }
63    }
64}
65
66impl Text {
67    /// Constructs the Text from raw byte contents.
68    ///
69    /// The expectation is that the buffer and length are valid and compatible.  That is,
70    /// that buffer is a valid pointer, that it points to an allocated buffer and that the length
71    /// of the allocated buffer is exactly `len`.
72    unsafe fn from_raw_bytes(buffer: *const raw::c_char, len: i64) -> Result<Self, common::Error> {
73        let mut status = common::Error::OK_CODE;
74        // Requires that 'bytes' is a valid pointer and len is the correct length of 'bytes'.
75        let rep = versioned_function!(utext_openUTF8)(0 as *mut UText, buffer, len, &mut status);
76        common::Error::ok_or_warning(status)?;
77        Ok(Text { rep })
78    }
79
80    /// Tries to produce a clone of this Text.
81    ///
82    /// If `deep` is set, a deep clone is made.   This is not a Clone trait since
83    /// this clone is parameterized, and may fail.
84    ///
85    /// Implements `utext_clone` from ICU4C.
86    pub fn try_clone(&self, deep: bool, readonly: bool) -> Result<Self, common::Error> {
87        let mut status = common::Error::OK_CODE;
88        // Requires that 'src' be a valid pointer.
89        let rep = unsafe {
90            assert!(common::Error::is_ok(status));
91            versioned_function!(utext_clone)(
92                0 as *mut UText,
93                self.rep,
94                deep as sys::UBool,
95                readonly as sys::UBool,
96                &mut status,
97            )
98        };
99        common::Error::ok_or_warning(status)?;
100        Ok(Text { rep })
101    }
102}
103
104impl Drop for Text {
105    /// Implements `utext_close` from ICU4C.
106    fn drop(&mut self) {
107        // Requires that self.rep is a valid pointer.
108        unsafe {
109            versioned_function!(utext_close)(self.rep);
110        }
111    }
112}
113
114#[cfg(test)]
115mod test {
116    use super::*;
117
118    #[test]
119    fn partial_eq() {
120        let foo = Text::try_from("foo".to_string()).expect("conversion from string succeeds.");
121        let bar = Text::try_from("foo").expect("conversion from literal succeeds");
122
123        let baz = Text::try_from("baz").expect("conversion from literal succeeds");
124
125        // Should all be equal to themselves.
126        assert_eq!(1i8, unsafe {
127            versioned_function!(utext_equals)(foo.rep, foo.rep)
128        });
129        assert_eq!(1i8, unsafe {
130            versioned_function!(utext_equals)(bar.rep, bar.rep)
131        });
132
133        // Should not be equal since not the same text and position.
134        assert_ne!(1i8, unsafe {
135            versioned_function!(utext_equals)(foo.rep, bar.rep)
136        });
137
138        assert_ne!(foo, bar);
139        assert_ne!(foo, baz);
140        assert_ne!(bar, baz);
141
142        assert_eq!(
143            foo,
144            foo.try_clone(true, true).expect("clone is a success"),
145            "a clone should be the same as its source"
146        );
147    }
148}