1use cxx::{type_id, ExternType};
7use std::cmp::Ordering;
8use std::fmt::{self, Write};
9use std::mem::MaybeUninit;
10
11use crate::{CaseSensitivity, QByteArray, QStringList, SplitBehaviorFlags};
12
13#[cxx::bridge]
14mod ffi {
15 #[namespace = "Qt"]
16 unsafe extern "C++" {
17 include!("cxx-qt-lib/qt.h");
18 type CaseSensitivity = crate::CaseSensitivity;
19 type SplitBehaviorFlags = crate::SplitBehaviorFlags;
20 }
21
22 unsafe extern "C++" {
23 include!("cxx-qt-lib/qbytearray.h");
24 type QByteArray = crate::QByteArray;
25 include!("cxx-qt-lib/qstring.h");
26 type QString = super::QString;
27 include!("cxx-qt-lib/qstringlist.h");
28 type QStringList = crate::QStringList;
29
30 fn append<'a>(self: &'a mut QString, str: &QString) -> &'a mut QString;
32
33 fn clear(self: &mut QString);
35
36 #[doc(hidden)]
38 #[rust_name = "compare_i32"]
39 fn compare(self: &QString, other: &QString, cs: CaseSensitivity) -> i32;
40
41 fn contains(self: &QString, str: &QString, cs: CaseSensitivity) -> bool;
45
46 #[rust_name = "ends_with"]
50 fn endsWith(self: &QString, s: &QString, cs: CaseSensitivity) -> bool;
51
52 #[rust_name = "is_empty"]
54 fn isEmpty(self: &QString) -> bool;
55
56 #[rust_name = "is_lower"]
58 fn isLower(self: &QString) -> bool;
59
60 #[rust_name = "is_null"]
62 fn isNull(self: &QString) -> bool;
63
64 #[rust_name = "is_right_to_left"]
66 fn isRightToLeft(self: &QString) -> bool;
67
68 #[rust_name = "is_upper"]
70 fn isUpper(self: &QString) -> bool;
71
72 #[rust_name = "is_valid_utf16"]
74 fn isValidUtf16(self: &QString) -> bool;
75
76 fn prepend<'a>(self: &'a mut QString, str: &QString) -> &'a mut QString;
78
79 fn remove<'a>(self: &'a mut QString, str: &QString, cs: CaseSensitivity)
83 -> &'a mut QString;
84
85 #[cfg(any(cxxqt_qt_version_at_least_7, cxxqt_qt_version_at_least_6_5))]
89 #[rust_name = "remove_first"]
90 fn removeFirst(self: &mut QString) -> &mut QString;
91
92 #[cfg(any(cxxqt_qt_version_at_least_7, cxxqt_qt_version_at_least_6_5))]
96 #[rust_name = "remove_last"]
97 fn removeLast(self: &mut QString) -> &mut QString;
98
99 fn replace<'a>(
103 self: &'a mut QString,
104 before: &QString,
105 after: &QString,
106 cs: CaseSensitivity,
107 ) -> &'a mut QString;
108
109 #[rust_name = "starts_with"]
111 fn startsWith(self: &QString, s: &QString, cs: CaseSensitivity) -> bool;
112
113 #[rust_name = "to_html_escaped"]
115 fn toHtmlEscaped(self: &QString) -> QString;
116 }
117
118 #[namespace = "rust::cxxqtlib1"]
119 unsafe extern "C++" {
120 include!("cxx-qt-lib/common.h");
121
122 #[doc(hidden)]
123 #[rust_name = "qstring_drop"]
124 fn drop(string: &mut QString);
125
126 #[doc(hidden)]
127 #[rust_name = "qstring_init_default"]
128 fn construct() -> QString;
129 #[doc(hidden)]
130 #[rust_name = "qstring_init_from_rust_string"]
131 fn qstringInitFromRustString(string: &str) -> QString;
132 #[doc(hidden)]
133 #[rust_name = "qstring_init_from_qstring"]
134 fn construct(string: &QString) -> QString;
135
136 #[doc(hidden)]
137 #[rust_name = "qstring_eq"]
138 fn operatorEq(a: &QString, b: &QString) -> bool;
139 #[doc(hidden)]
140 #[rust_name = "qstring_cmp"]
141 fn operatorCmp(a: &QString, b: &QString) -> i8;
142
143 #[doc(hidden)]
144 #[rust_name = "qstring_as_slice"]
145 fn qstringAsSlice(string: &QString) -> &[u16];
146
147 #[doc(hidden)]
148 #[rust_name = "qstring_arg"]
149 fn qstringArg(string: &QString, a: &QString) -> QString;
150 #[doc(hidden)]
151 #[rust_name = "qstring_index_of"]
152 fn qstringIndexOf(
153 string: &QString,
154 str: &QString,
155 from: isize,
156 cs: CaseSensitivity,
157 ) -> isize;
158 #[doc(hidden)]
159 #[rust_name = "qstring_insert"]
160 fn qstringInsert<'a>(string: &'a mut QString, pos: isize, str: &QString)
161 -> &'a mut QString;
162 #[doc(hidden)]
163 #[rust_name = "qstring_left"]
164 fn qstringLeft(string: &QString, n: isize) -> QString;
165 #[doc(hidden)]
166 #[rust_name = "qstring_len"]
167 fn qstringLen(string: &QString) -> isize;
168 #[doc(hidden)]
169 #[rust_name = "qstring_mid"]
170 fn qstringMid(string: &QString, position: isize, n: isize) -> QString;
171 #[doc(hidden)]
172 #[rust_name = "qstring_right"]
173 fn qstringRight(string: &QString, n: isize) -> QString;
174 #[doc(hidden)]
175 #[rust_name = "qstring_simplified"]
176 fn qstringSimplified(string: &QString) -> QString;
177 #[doc(hidden)]
178 #[rust_name = "qstring_split"]
179 fn qstringSplit(
180 string: &QString,
181 sep: &QString,
182 behavior: SplitBehaviorFlags,
183 cs: CaseSensitivity,
184 ) -> QStringList;
185 #[doc(hidden)]
186 #[rust_name = "qstring_to_latin1"]
187 fn qstringToLatin1(string: &QString) -> QByteArray;
188 #[doc(hidden)]
189 #[rust_name = "qstring_to_local8bit"]
190 fn qstringToLocal8Bit(string: &QString) -> QByteArray;
191 #[doc(hidden)]
192 #[rust_name = "qstring_to_lower"]
193 fn qstringToLower(string: &QString) -> QString;
194 #[doc(hidden)]
195 #[rust_name = "qstring_to_upper"]
196 fn qstringToUpper(string: &QString) -> QString;
197 #[doc(hidden)]
198 #[rust_name = "qstring_to_utf8"]
199 fn qstringToUtf8(string: &QString) -> QByteArray;
200 #[doc(hidden)]
201 #[rust_name = "qstring_trimmed"]
202 fn qstringTrimmed(string: &QString) -> QString;
203 }
204}
205
206#[repr(C)]
212pub struct QString {
213 _d: MaybeUninit<usize>,
218 #[cfg(cxxqt_qt_version_major = "6")]
219 _ptr: MaybeUninit<usize>,
220 #[cfg(cxxqt_qt_version_major = "6")]
221 _size: MaybeUninit<isize>,
222}
223
224impl Clone for QString {
225 fn clone(&self) -> Self {
231 ffi::qstring_init_from_qstring(self)
232 }
233}
234
235impl Default for QString {
236 fn default() -> Self {
238 ffi::qstring_init_default()
239 }
240}
241
242impl PartialEq for QString {
243 fn eq(&self, other: &Self) -> bool {
244 ffi::qstring_eq(self, other)
245 }
246}
247
248impl Eq for QString {}
249
250impl PartialOrd for QString {
251 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
252 Some(self.cmp(other))
253 }
254}
255
256impl Ord for QString {
257 fn cmp(&self, other: &Self) -> Ordering {
258 ffi::qstring_cmp(self, other).cmp(&0)
259 }
260}
261
262impl fmt::Display for QString {
263 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
267 if f.width().is_some() || f.precision().is_some() {
268 return f.pad(&String::from(self));
269 }
270 for c in char::decode_utf16(self.as_slice().iter().copied()) {
271 f.write_char(c.unwrap_or(char::REPLACEMENT_CHARACTER))?;
272 }
273 Ok(())
274 }
275}
276
277impl fmt::Debug for QString {
278 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
279 String::from(self).fmt(f)
280 }
281}
282
283impl std::ops::Add for QString {
284 type Output = Self;
285 fn add(self, other: Self) -> Self {
286 let mut res = ffi::qstring_init_from_qstring(&self);
287 res.append(&other);
288 res
289 }
290}
291
292impl Drop for QString {
293 fn drop(&mut self) {
295 ffi::qstring_drop(self)
296 }
297}
298
299impl From<&str> for QString {
300 fn from(str: &str) -> Self {
304 ffi::qstring_init_from_rust_string(str)
305 }
306}
307
308impl From<&String> for QString {
309 fn from(str: &String) -> Self {
313 ffi::qstring_init_from_rust_string(str)
314 }
315}
316
317impl From<String> for QString {
318 fn from(str: String) -> Self {
322 ffi::qstring_init_from_rust_string(&str)
323 }
324}
325
326impl From<&QString> for String {
327 fn from(qstring: &QString) -> Self {
331 String::from_utf16_lossy(qstring.as_slice())
332 }
333}
334
335impl From<QString> for String {
336 fn from(qstring: QString) -> Self {
340 String::from_utf16_lossy(qstring.as_slice())
341 }
342}
343
344impl QString {
345 pub fn arg(&self, a: &QString) -> Self {
349 ffi::qstring_arg(self, a)
350 }
351
352 pub fn as_slice(&self) -> &[u16] {
354 ffi::qstring_as_slice(self)
355 }
356
357 pub fn compare(&self, other: &QString, cs: CaseSensitivity) -> Ordering {
363 self.compare_i32(other, cs).cmp(&0)
364 }
365
366 pub fn index_of(&self, str: &QString, from: isize, cs: CaseSensitivity) -> isize {
371 ffi::qstring_index_of(self, str, from, cs)
372 }
373
374 pub fn insert<'a>(&'a mut self, position: isize, str: &Self) -> &'a mut Self {
376 ffi::qstring_insert(self, position, str)
377 }
378
379 pub fn left(&self, n: isize) -> Self {
381 ffi::qstring_left(self, n)
382 }
383
384 pub fn len(self: &QString) -> isize {
386 ffi::qstring_len(self)
387 }
388
389 pub fn mid(&self, position: isize, n: isize) -> Self {
391 ffi::qstring_mid(self, position, n)
392 }
393
394 pub fn right(&self, n: isize) -> Self {
396 ffi::qstring_right(self, n)
397 }
398
399 pub fn simplified(&self) -> Self {
404 ffi::qstring_simplified(self)
405 }
406
407 pub fn split(
414 &self,
415 sep: &QString,
416 behavior: SplitBehaviorFlags,
417 cs: CaseSensitivity,
418 ) -> QStringList {
419 ffi::qstring_split(self, sep, behavior, cs)
420 }
421
422 pub fn to_latin1(&self) -> QByteArray {
426 ffi::qstring_to_latin1(self)
427 }
428
429 pub fn to_local8bit(&self) -> QByteArray {
433 ffi::qstring_to_local8bit(self)
434 }
435
436 pub fn to_lower(&self) -> Self {
438 ffi::qstring_to_lower(self)
439 }
440
441 pub fn to_upper(&self) -> Self {
443 ffi::qstring_to_upper(self)
444 }
445
446 pub fn to_utf8(&self) -> QByteArray {
448 ffi::qstring_to_utf8(self)
449 }
450
451 pub fn trimmed(&self) -> Self {
455 ffi::qstring_trimmed(self)
456 }
457}
458
459unsafe impl ExternType for QString {
463 type Id = type_id!("QString");
464 type Kind = cxx::kind::Trivial;
465}
466
467#[cfg(feature = "serde")]
468impl serde::Serialize for QString {
469 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
470 serializer.serialize_str(&String::from(self))
471 }
472}
473
474#[cfg(feature = "serde")]
475impl<'de> serde::Deserialize<'de> for QString {
476 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
477 use serde::de::{Error as DeError, Unexpected, Visitor};
478
479 struct StringVisitor;
480
481 impl Visitor<'_> for StringVisitor {
482 type Value = QString;
483
484 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
485 formatter.write_str("a string")
486 }
487
488 fn visit_str<E: DeError>(self, v: &str) -> Result<Self::Value, E> {
489 Ok(Self::Value::from(v))
490 }
491
492 fn visit_bytes<E: DeError>(self, v: &[u8]) -> Result<Self::Value, E> {
493 match std::str::from_utf8(v) {
494 Ok(s) => Ok(Self::Value::from(s)),
495 Err(_) => Err(E::invalid_value(Unexpected::Bytes(v), &self)),
496 }
497 }
498 }
499
500 let visitor = StringVisitor;
501 deserializer.deserialize_string(visitor)
502 }
503}
504
505#[cfg(test)]
506mod test {
507 use super::*;
508
509 #[cfg(feature = "serde")]
510 #[test]
511 fn qstring_serde() {
512 let qstring = QString::from("KDAB");
513 assert_eq!(crate::serde_impl::roundtrip(&qstring), qstring);
514 }
515
516 #[test]
517 fn test_ordering() {
518 let qstring_a = QString::from("a");
519 let qstring_b = QString::from("b");
520
521 assert!(qstring_a < qstring_b);
522 assert_eq!(qstring_a.cmp(&qstring_b), Ordering::Less);
523 assert_eq!(qstring_b.cmp(&qstring_a), Ordering::Greater);
524 assert_eq!(qstring_a.cmp(&qstring_a), Ordering::Equal);
525
526 assert_eq!(
527 qstring_a.compare(&qstring_b, crate::CaseSensitivity::CaseInsensitive),
528 Ordering::Less
529 );
530 assert_eq!(
531 qstring_b.compare(&qstring_a, crate::CaseSensitivity::CaseInsensitive),
532 Ordering::Greater
533 );
534 assert_eq!(
535 qstring_a.compare(&qstring_a, crate::CaseSensitivity::CaseInsensitive),
536 Ordering::Equal
537 );
538 }
539}