cxx_qt_lib/core/
qstring.rs

1// SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
3// SPDX-FileContributor: Gerhard de Clercq <gerhard.declercq@kdab.com>
4//
5// SPDX-License-Identifier: MIT OR Apache-2.0
6use cxx::{type_id, ExternType};
7use std::cmp::Ordering;
8use std::fmt;
9use std::mem::MaybeUninit;
10
11#[cxx::bridge]
12mod ffi {
13    #[namespace = "Qt"]
14    unsafe extern "C++" {
15        include!("cxx-qt-lib/qt.h");
16        type CaseSensitivity = crate::CaseSensitivity;
17        type SplitBehaviorFlags = crate::SplitBehaviorFlags;
18    }
19
20    unsafe extern "C++" {
21        include!("cxx-qt-lib/qbytearray.h");
22        type QByteArray = crate::QByteArray;
23        include!("cxx-qt-lib/qstring.h");
24        type QString = super::QString;
25        include!("cxx-qt-lib/qstringlist.h");
26        type QStringList = crate::QStringList;
27
28        /// Appends the string str onto the end of this string.
29        fn append<'a>(self: &'a mut QString, str: &QString) -> &'a mut QString;
30
31        /// Clears the contents of the string and makes it null.
32        fn clear(self: &mut QString);
33
34        // We wrap this method to provide an enum so hide it from docs
35        #[doc(hidden)]
36        #[rust_name = "compare_i32"]
37        fn compare(self: &QString, other: &QString, cs: CaseSensitivity) -> i32;
38
39        /// Returns true if this string contains an occurrence of the string str; otherwise returns false.
40        fn contains(self: &QString, str: &QString, cs: CaseSensitivity) -> bool;
41
42        /// Returns true if the string ends with s; otherwise returns false.
43        #[rust_name = "ends_with"]
44        fn endsWith(self: &QString, s: &QString, cs: CaseSensitivity) -> bool;
45
46        /// Returns true if the string has no characters; otherwise returns false.
47        #[rust_name = "is_empty"]
48        fn isEmpty(self: &QString) -> bool;
49
50        /// Returns true if the string is lowercase, that is, it's identical to its toLower() folding.
51        #[rust_name = "is_lower"]
52        fn isLower(self: &QString) -> bool;
53
54        /// Returns true if this string is null; otherwise returns false.
55        #[rust_name = "is_null"]
56        fn isNull(self: &QString) -> bool;
57
58        /// Returns true if the string is read right to left.
59        #[rust_name = "is_right_to_left"]
60        fn isRightToLeft(self: &QString) -> bool;
61
62        /// Returns true if the string is uppercase, that is, it's identical to its toUpper() folding.
63        #[rust_name = "is_upper"]
64        fn isUpper(self: &QString) -> bool;
65
66        /// Returns true if the string contains valid UTF-16 encoded data, or false otherwise.
67        #[rust_name = "is_valid_utf16"]
68        fn isValidUtf16(self: &QString) -> bool;
69
70        /// Prepends the string str to the beginning of this string and returns a reference to this string.
71        fn prepend<'a>(self: &'a mut QString, str: &QString) -> &'a mut QString;
72
73        /// Removes every occurrence of the given str string in this string, and returns a reference to this string.
74        fn remove<'a>(self: &'a mut QString, str: &QString, cs: CaseSensitivity)
75            -> &'a mut QString;
76
77        /// Removes the first character in this string. If the string is empty, this function does nothing.
78        /// This function was introduced in Qt 6.5.
79        #[cfg(any(cxxqt_qt_version_at_least_7, cxxqt_qt_version_at_least_6_5))]
80        #[rust_name = "remove_first"]
81        fn removeFirst(self: &mut QString) -> &mut QString;
82
83        /// Removes the last character in this string. If the string is empty, this function does nothing.
84        /// This function was introduced in Qt 6.5.
85        #[cfg(any(cxxqt_qt_version_at_least_7, cxxqt_qt_version_at_least_6_5))]
86        #[rust_name = "remove_last"]
87        fn removeLast(self: &mut QString) -> &mut QString;
88
89        /// Replaces every occurrence of the string before with the string after and returns a reference to this string.
90        fn replace<'a>(
91            self: &'a mut QString,
92            before: &QString,
93            after: &QString,
94            cs: CaseSensitivity,
95        ) -> &'a mut QString;
96
97        /// Returns true if the string starts with s; otherwise returns false.
98        #[rust_name = "starts_with"]
99        fn startsWith(self: &QString, s: &QString, cs: CaseSensitivity) -> bool;
100
101        /// Converts a plain text string to an HTML string with HTML metacharacters <, >, &, and " replaced by HTML entities.
102        #[rust_name = "to_html_escaped"]
103        fn toHtmlEscaped(self: &QString) -> QString;
104    }
105
106    #[namespace = "rust::cxxqtlib1"]
107    unsafe extern "C++" {
108        include!("cxx-qt-lib/common.h");
109
110        #[doc(hidden)]
111        #[rust_name = "qstring_drop"]
112        fn drop(string: &mut QString);
113
114        #[doc(hidden)]
115        #[rust_name = "qstring_init_default"]
116        fn construct() -> QString;
117        #[doc(hidden)]
118        #[rust_name = "qstring_init_from_rust_string"]
119        fn qstringInitFromRustString(string: &str) -> QString;
120        #[doc(hidden)]
121        #[rust_name = "qstring_init_from_qstring"]
122        fn construct(string: &QString) -> QString;
123
124        #[doc(hidden)]
125        #[rust_name = "qstring_eq"]
126        fn operatorEq(a: &QString, b: &QString) -> bool;
127        #[doc(hidden)]
128        #[rust_name = "qstring_cmp"]
129        fn operatorCmp(a: &QString, b: &QString) -> i8;
130
131        #[doc(hidden)]
132        #[rust_name = "qstring_to_rust_string"]
133        fn qstringToRustString(string: &QString) -> String;
134
135        #[doc(hidden)]
136        #[rust_name = "qstring_arg"]
137        fn qstringArg(string: &QString, a: &QString) -> QString;
138        #[doc(hidden)]
139        #[rust_name = "qstring_index_of"]
140        fn qstringIndexOf(
141            string: &QString,
142            str: &QString,
143            from: isize,
144            cs: CaseSensitivity,
145        ) -> isize;
146        #[doc(hidden)]
147        #[rust_name = "qstring_insert"]
148        fn qstringInsert<'a>(string: &'a mut QString, pos: isize, str: &QString)
149            -> &'a mut QString;
150        #[doc(hidden)]
151        #[rust_name = "qstring_left"]
152        fn qstringLeft(string: &QString, n: isize) -> QString;
153        #[doc(hidden)]
154        #[rust_name = "qstring_len"]
155        fn qstringLen(string: &QString) -> isize;
156        #[doc(hidden)]
157        #[rust_name = "qstring_mid"]
158        fn qstringMid(string: &QString, position: isize, n: isize) -> QString;
159        #[doc(hidden)]
160        #[rust_name = "qstring_right"]
161        fn qstringRight(string: &QString, n: isize) -> QString;
162        #[doc(hidden)]
163        #[rust_name = "qstring_simplified"]
164        fn qstringSimplified(string: &QString) -> QString;
165        #[doc(hidden)]
166        #[rust_name = "qstring_split"]
167        fn qstringSplit(
168            string: &QString,
169            sep: &QString,
170            behavior: SplitBehaviorFlags,
171            cs: CaseSensitivity,
172        ) -> QStringList;
173        #[doc(hidden)]
174        #[rust_name = "qstring_to_latin1"]
175        fn qstringToLatin1(string: &QString) -> QByteArray;
176        #[doc(hidden)]
177        #[rust_name = "qstring_to_local8bit"]
178        fn qstringToLocal8Bit(string: &QString) -> QByteArray;
179        #[doc(hidden)]
180        #[rust_name = "qstring_to_lower"]
181        fn qstringToLower(string: &QString) -> QString;
182        #[doc(hidden)]
183        #[rust_name = "qstring_to_upper"]
184        fn qstringToUpper(string: &QString) -> QString;
185        #[doc(hidden)]
186        #[rust_name = "qstring_to_utf8"]
187        fn qstringToUtf8(string: &QString) -> QByteArray;
188        #[doc(hidden)]
189        #[rust_name = "qstring_trimmed"]
190        fn qstringTrimmed(string: &QString) -> QString;
191    }
192}
193
194#[cfg(feature = "serde")]
195use serde::{Deserialize, Serialize};
196
197/// The QString class provides a Unicode character string.
198///
199/// Note that QString is a UTF-16 whereas Rust strings are a UTF-8
200#[repr(C)]
201#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
202#[cfg_attr(feature = "serde", serde(from = "String", into = "String"))]
203pub struct QString {
204    /// The layout has changed between Qt 5 and Qt 6
205    ///
206    /// Qt5 QString has one pointer as a member
207    /// Qt6 QString has one member, which contains two pointers and a size_t
208    #[cfg(cxxqt_qt_version_major = "5")]
209    _space: MaybeUninit<usize>,
210    #[cfg(cxxqt_qt_version_major = "6")]
211    _space: MaybeUninit<[usize; 3]>,
212}
213
214impl Clone for QString {
215    /// Constructs a copy of other.
216    ///
217    /// This operation takes constant time, because QString is implicitly shared.
218    /// This makes returning a QString from a function very fast.
219    /// If a shared instance is modified, it will be copied (copy-on-write), and that takes linear time.
220    fn clone(&self) -> Self {
221        ffi::qstring_init_from_qstring(self)
222    }
223}
224
225impl Default for QString {
226    /// Constructs a null string. Null strings are also empty.
227    fn default() -> Self {
228        ffi::qstring_init_default()
229    }
230}
231
232impl PartialEq for QString {
233    fn eq(&self, other: &Self) -> bool {
234        ffi::qstring_eq(self, other)
235    }
236}
237
238impl Eq for QString {}
239
240impl PartialOrd for QString {
241    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
242        Some(self.cmp(other))
243    }
244}
245
246impl Ord for QString {
247    fn cmp(&self, other: &Self) -> Ordering {
248        ffi::qstring_cmp(self, other).cmp(&0)
249    }
250}
251
252impl fmt::Display for QString {
253    /// Convert the QString to a Rust string
254    ///
255    /// Note that this converts from UTF-16 to UTF-8
256    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
257        write!(f, "{}", <&QString as Into<String>>::into(self))
258    }
259}
260
261impl fmt::Debug for QString {
262    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
263        write!(f, "{self}")
264    }
265}
266
267impl std::ops::Add for QString {
268    type Output = Self;
269    fn add(self, other: Self) -> Self {
270        let mut res = ffi::qstring_init_from_qstring(&self);
271        res.append(&other);
272        res
273    }
274}
275
276impl Drop for QString {
277    /// Destroys the string.
278    fn drop(&mut self) {
279        ffi::qstring_drop(self)
280    }
281}
282
283impl From<&str> for QString {
284    /// Constructs a QString from a Rust string
285    ///
286    /// Note that this converts from UTF-8 to UTF-16
287    fn from(str: &str) -> Self {
288        ffi::qstring_init_from_rust_string(str)
289    }
290}
291
292impl From<&String> for QString {
293    /// Constructs a QString from a Rust string
294    ///
295    /// Note that this converts from UTF-8 to UTF-16
296    fn from(str: &String) -> Self {
297        ffi::qstring_init_from_rust_string(str)
298    }
299}
300
301impl From<String> for QString {
302    /// Constructs a QString from a Rust string
303    ///
304    /// Note that this converts from UTF-8 to UTF-16
305    fn from(str: String) -> Self {
306        ffi::qstring_init_from_rust_string(&str)
307    }
308}
309
310impl From<&QString> for String {
311    /// Convert the QString to a Rust string
312    ///
313    /// Note that this converts from UTF-16 to UTF-8
314    fn from(qstring: &QString) -> Self {
315        ffi::qstring_to_rust_string(qstring)
316    }
317}
318
319impl From<QString> for String {
320    /// Convert the QString to a Rust string
321    ///
322    /// Note that this converts from UTF-16 to UTF-8
323    fn from(qstring: QString) -> Self {
324        ffi::qstring_to_rust_string(&qstring)
325    }
326}
327
328impl QString {
329    /// Returns a copy of this string with the lowest numbered place marker replaced by string a, i.e., %1, %2, ..., %99.
330    pub fn arg(&self, a: &QString) -> Self {
331        ffi::qstring_arg(self, a)
332    }
333
334    /// Lexically compares this string with the other string and
335    /// returns if this string is less than, equal to, or greater than the other string.
336    pub fn compare(&self, other: &QString, cs: ffi::CaseSensitivity) -> Ordering {
337        self.compare_i32(other, cs).cmp(&0)
338    }
339
340    /// Returns the index position of the first occurrence of the string str in this string,
341    /// searching forward from index position from. Returns -1 if str is not found.
342    pub fn index_of(&self, str: &QString, from: isize, cs: ffi::CaseSensitivity) -> isize {
343        ffi::qstring_index_of(self, str, from, cs)
344    }
345
346    /// Inserts the string str at the given index position and returns a mutable reference to this string.
347    pub fn insert<'a>(&'a mut self, pos: isize, str: &Self) -> &'a mut Self {
348        ffi::qstring_insert(self, pos, str)
349    }
350
351    /// Returns a substring that contains the n leftmost characters of the string.
352    pub fn left(&self, n: isize) -> Self {
353        ffi::qstring_left(self, n)
354    }
355
356    /// Returns the number of characters in this string.
357    pub fn len(self: &QString) -> isize {
358        ffi::qstring_len(self)
359    }
360
361    /// Returns a string that contains n characters of this string, starting at the specified position index.
362    pub fn mid(&self, position: isize, n: isize) -> Self {
363        ffi::qstring_mid(self, position, n)
364    }
365
366    /// Returns a substring that contains the n rightmost characters of the string.
367    pub fn right(&self, n: isize) -> Self {
368        ffi::qstring_right(self, n)
369    }
370
371    /// Returns a string that has whitespace removed from the start and the end,
372    /// and that has each sequence of internal whitespace replaced with a single space.
373    pub fn simplified(&self) -> Self {
374        ffi::qstring_simplified(self)
375    }
376
377    /// Splits the string into substrings wherever sep occurs, and returns the list of those strings.
378    /// If sep does not match anywhere in the string, split() returns a single-element list containing this string.
379    pub fn split(
380        &self,
381        sep: &QString,
382        behavior: ffi::SplitBehaviorFlags,
383        cs: ffi::CaseSensitivity,
384    ) -> ffi::QStringList {
385        ffi::qstring_split(self, sep, behavior, cs)
386    }
387
388    /// Returns a Latin-1 representation of the string as a QByteArray.
389    pub fn to_latin1(&self) -> ffi::QByteArray {
390        ffi::qstring_to_latin1(self)
391    }
392
393    /// Returns the local 8-bit representation of the string as a QByteArray.
394    /// The returned byte array is undefined if the string contains characters not supported by the local 8-bit encoding.
395    pub fn to_local8bit(&self) -> ffi::QByteArray {
396        ffi::qstring_to_local8bit(self)
397    }
398
399    /// Returns a lowercase copy of the string.
400    pub fn to_lower(&self) -> Self {
401        ffi::qstring_to_lower(self)
402    }
403
404    /// Returns an uppercase copy of the string.
405    pub fn to_upper(&self) -> Self {
406        ffi::qstring_to_upper(self)
407    }
408
409    /// Returns a UTF-8 representation of the string as a QByteArray.
410    pub fn to_utf8(&self) -> ffi::QByteArray {
411        ffi::qstring_to_utf8(self)
412    }
413
414    /// Returns a string that has whitespace removed from the start and the end.
415    pub fn trimmed(&self) -> Self {
416        ffi::qstring_trimmed(self)
417    }
418}
419
420// Safety:
421//
422// Static checks on the C++ side to ensure the size is the same.
423unsafe impl ExternType for QString {
424    type Id = type_id!("QString");
425    type Kind = cxx::kind::Trivial;
426}
427
428#[cfg(test)]
429mod test {
430    use super::*;
431
432    #[test]
433    fn test_ordering() {
434        let qstring_a = QString::from("a");
435        let qstring_b = QString::from("b");
436
437        assert!(qstring_a < qstring_b);
438        assert_eq!(qstring_a.cmp(&qstring_b), Ordering::Less);
439        assert_eq!(qstring_b.cmp(&qstring_a), Ordering::Greater);
440        assert_eq!(qstring_a.cmp(&qstring_a), Ordering::Equal);
441
442        assert_eq!(
443            qstring_a.compare(&qstring_b, crate::CaseSensitivity::CaseInsensitive),
444            Ordering::Less
445        );
446        assert_eq!(
447            qstring_b.compare(&qstring_a, crate::CaseSensitivity::CaseInsensitive),
448            Ordering::Greater
449        );
450        assert_eq!(
451            qstring_a.compare(&qstring_a, crate::CaseSensitivity::CaseInsensitive),
452            Ordering::Equal
453        );
454    }
455}