blob: 501017591fe55687fe7f6eb3583b01a77cc8a85b [file] [log] [blame]
David Tolnay3384c142020-09-14 00:26:47 -04001use alloc::borrow::Cow;
David Tolnayc5a52f92020-09-14 00:43:29 -04002use alloc::string::String;
David Tolnay3384c142020-09-14 00:26:47 -04003use core::fmt::{self, Debug, Display};
4use core::slice;
5use core::str::{self, Utf8Error};
David Tolnay7db73692019-10-20 14:51:12 -04006
7extern "C" {
David Tolnay8f16ae72020-10-08 18:21:13 -07008 #[link_name = "cxxbridge05$cxx_string$data"]
David Tolnay90691f42020-11-14 20:01:46 -08009 fn string_data(this: &CxxString) -> *const u8;
David Tolnay8f16ae72020-10-08 18:21:13 -070010 #[link_name = "cxxbridge05$cxx_string$length"]
David Tolnay90691f42020-11-14 20:01:46 -080011 fn string_length(this: &CxxString) -> usize;
12 #[link_name = "cxxbridge05$cxx_string$push"]
13 fn string_push(this: &mut CxxString, ptr: *const u8, len: usize);
David Tolnay7db73692019-10-20 14:51:12 -040014}
15
16/// Binding to C++ `std::string`.
17///
18/// # Invariants
19///
20/// As an invariant of this API and the static analysis of the cxx::bridge
21/// macro, in Rust code we can never obtain a `CxxString` by value. C++'s string
22/// requires a move constructor and may hold internal pointers, which is not
23/// compatible with Rust's move behavior. Instead in Rust code we will only ever
24/// look at a CxxString through a reference or smart pointer, as in `&CxxString`
25/// or `UniquePtr<CxxString>`.
26#[repr(C)]
27pub struct CxxString {
28 _private: [u8; 0],
29}
30
31impl CxxString {
32 /// Returns the length of the string in bytes.
33 ///
34 /// Matches the behavior of C++ [std::string::size][size].
35 ///
36 /// [size]: https://en.cppreference.com/w/cpp/string/basic_string/size
37 pub fn len(&self) -> usize {
38 unsafe { string_length(self) }
39 }
40
41 /// Returns true if `self` has a length of zero bytes.
David Tolnayd7b8a6e2020-04-24 16:22:55 -070042 ///
43 /// Matches the behavior of C++ [std::string::empty][empty].
44 ///
45 /// [empty]: https://en.cppreference.com/w/cpp/string/basic_string/empty
David Tolnay7db73692019-10-20 14:51:12 -040046 pub fn is_empty(&self) -> bool {
47 self.len() == 0
48 }
49
50 /// Returns a byte slice of this string's contents.
51 pub fn as_bytes(&self) -> &[u8] {
52 let data = self.as_ptr();
53 let len = self.len();
54 unsafe { slice::from_raw_parts(data, len) }
55 }
56
57 /// Produces a pointer to the first character of the string.
58 ///
59 /// Matches the behavior of C++ [std::string::data][data].
60 ///
61 /// Note that the return type may look like `const char *` but is not a
62 /// `const char *` in the typical C sense, as C++ strings may contain
63 /// internal null bytes. As such, the returned pointer only makes sense as a
David Tolnay3cd990f2020-04-24 16:24:26 -070064 /// string in combination with the length returned by [`len()`][len].
David Tolnay7db73692019-10-20 14:51:12 -040065 ///
66 /// [data]: https://en.cppreference.com/w/cpp/string/basic_string/data
David Tolnay3cd990f2020-04-24 16:24:26 -070067 /// [len]: #method.len
David Tolnay7db73692019-10-20 14:51:12 -040068 pub fn as_ptr(&self) -> *const u8 {
69 unsafe { string_data(self) }
70 }
71
72 /// Validates that the C++ string contains UTF-8 data and produces a view of
73 /// it as a Rust &amp;str, otherwise an error.
74 pub fn to_str(&self) -> Result<&str, Utf8Error> {
75 str::from_utf8(self.as_bytes())
76 }
77
78 /// If the contents of the C++ string are valid UTF-8, this function returns
79 /// a view as a Cow::Borrowed &amp;str. Otherwise replaces any invalid UTF-8
80 /// sequences with the U+FFFD [replacement character] and returns a
81 /// Cow::Owned String.
82 ///
83 /// [replacement character]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html
84 pub fn to_string_lossy(&self) -> Cow<str> {
85 String::from_utf8_lossy(self.as_bytes())
86 }
David Tolnay90691f42020-11-14 20:01:46 -080087
88 /// Appends a given string slice onto the end of this C++ string.
89 pub fn push_str(&mut self, s: &str) {
David Tolnay95e74b32020-11-14 20:16:22 -080090 self.push_bytes(s.as_bytes());
91 }
92
93 /// Appends arbitrary bytes onto the end of this C++ string.
94 pub fn push_bytes(&mut self, bytes: &[u8]) {
95 unsafe { string_push(self, bytes.as_ptr(), bytes.len()) }
David Tolnay90691f42020-11-14 20:01:46 -080096 }
David Tolnay7db73692019-10-20 14:51:12 -040097}
98
99impl Display for CxxString {
100 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
David Tolnayd930a792020-03-25 12:24:40 -0700101 Display::fmt(self.to_string_lossy().as_ref(), f)
David Tolnay7db73692019-10-20 14:51:12 -0400102 }
103}
104
105impl Debug for CxxString {
106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
David Tolnayd930a792020-03-25 12:24:40 -0700107 Debug::fmt(self.to_string_lossy().as_ref(), f)
David Tolnay7db73692019-10-20 14:51:12 -0400108 }
109}
David Tolnay42ebfa22020-03-25 12:26:22 -0700110
111impl PartialEq for CxxString {
112 fn eq(&self, other: &CxxString) -> bool {
113 self.as_bytes() == other.as_bytes()
114 }
115}
116
117impl PartialEq<CxxString> for str {
118 fn eq(&self, other: &CxxString) -> bool {
119 self.as_bytes() == other.as_bytes()
120 }
121}
122
123impl PartialEq<str> for CxxString {
124 fn eq(&self, other: &str) -> bool {
125 self.as_bytes() == other.as_bytes()
126 }
127}