1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use {
    rust_icu_common as common, rust_icu_sys as sys, rust_icu_sys::versioned_function,
    rust_icu_sys::*, std::cmp::Eq, std::convert::TryFrom, std::os::raw,
};

#[derive(Debug)]
pub struct Text {
    // rep is allocated by the underlying library and has to be dropped using `utext_close`.
    rep: *mut UText,
}

impl PartialEq for Text {
    fn eq(&self, other: &Self) -> bool {
        let ret = unsafe { versioned_function!(utext_equals)(self.rep, other.rep) };
        match ret {
            0 => false,
            1 => true,
            // Could be a bug in the underlying library.
            _ => panic!("value is not convertible to bool in Text::eq: {}", ret),
        }
    }
}
impl Eq for Text {}

impl TryFrom<String> for Text {
    type Error = common::Error;

    /// Produces a unicode Text from a rust String.
    ///
    /// The conversion may fail if the string is not well formed, and may result in an error.
    ///
    /// Implements `utext_open` from ICU4C.
    fn try_from(s: String) -> Result<Self, Self::Error> {
        let len: i64 = s.len() as i64;
        let bytes = s.as_ptr() as *const raw::c_char;
        // bytes and len must be compatible.  Ensured by the two lines just above.
        unsafe { Self::from_raw_bytes(bytes, len) }
    }
}

impl TryFrom<&str> for Text {
    type Error = common::Error;
    /// Implements `utext_open`
    fn try_from(s: &str) -> Result<Self, Self::Error> {
        let len = s.len() as i64;
        let bytes = s.as_ptr() as *const raw::c_char;
        // bytes and len must be compatible.  Ensured by the two lines just above.
        unsafe { Self::from_raw_bytes(bytes, len) }
    }
}

impl Text {
    /// Constructs the Text from raw byte contents.
    ///
    /// The expectation is that the buffer and length are valid and compatible.  That is,
    /// that buffer is a valid pointer, that it points to an allocated buffer and that the length
    /// of the allocated buffer is exactly `len`.
    unsafe fn from_raw_bytes(buffer: *const raw::c_char, len: i64) -> Result<Self, common::Error> {
        let mut status = common::Error::OK_CODE;
        // Requires that 'bytes' is a valid pointer and len is the correct length of 'bytes'.
        let rep = versioned_function!(utext_openUTF8)(0 as *mut UText, buffer, len, &mut status);
        common::Error::ok_or_warning(status)?;
        Ok(Text { rep })
    }

    /// Tries to produce a clone of this Text.
    ///
    /// If `deep` is set, a deep clone is made.   This is not a Clone trait since
    /// this clone is parameterized, and may fail.
    ///
    /// Implements `utext_clone` from ICU4C.
    pub fn try_clone(&self, deep: bool, readonly: bool) -> Result<Self, common::Error> {
        let mut status = common::Error::OK_CODE;
        // Requires that 'src' be a valid pointer.
        let rep = unsafe {
            assert!(common::Error::is_ok(status));
            versioned_function!(utext_clone)(
                0 as *mut UText,
                self.rep,
                deep as sys::UBool,
                readonly as sys::UBool,
                &mut status,
            )
        };
        common::Error::ok_or_warning(status)?;
        Ok(Text { rep })
    }
}

impl Drop for Text {
    /// Implements `utext_close` from ICU4C.
    fn drop(&mut self) {
        // Requires that self.rep is a valid pointer.
        unsafe {
            versioned_function!(utext_close)(self.rep);
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn partial_eq() {
        let foo = Text::try_from("foo".to_string()).expect("conversion from string succeeds.");
        let bar = Text::try_from("foo").expect("conversion from literal succeeds");

        let baz = Text::try_from("baz").expect("conversion from literal succeeds");

        // Should all be equal to themselves.
        assert_eq!(1i8, unsafe {
            versioned_function!(utext_equals)(foo.rep, foo.rep)
        });
        assert_eq!(1i8, unsafe {
            versioned_function!(utext_equals)(bar.rep, bar.rep)
        });

        // Should not be equal since not the same text and position.
        assert_ne!(1i8, unsafe {
            versioned_function!(utext_equals)(foo.rep, bar.rep)
        });

        assert_ne!(foo, bar);
        assert_ne!(foo, baz);
        assert_ne!(bar, baz);

        assert_eq!(
            foo,
            foo.try_clone(true, true).expect("clone is a success"),
            "a clone should be the same as its source"
        );
    }
}