blob: baad468e66a3abd25268c37b6ea0e7e0fc7e4655 [file] [log] [blame]
David Tolnay7db73692019-10-20 14:51:12 -04001use std::borrow::Cow;
2use std::fmt::{self, Debug, Display};
3use std::slice;
4use std::str::{self, Utf8Error};
5
6extern "C" {
David Tolnay8c730492020-03-13 01:29:06 -07007 #[link_name = "cxxbridge02$cxx_string$data"]
David Tolnay7db73692019-10-20 14:51:12 -04008 fn string_data(_: &CxxString) -> *const u8;
David Tolnay8c730492020-03-13 01:29:06 -07009 #[link_name = "cxxbridge02$cxx_string$length"]
David Tolnay7db73692019-10-20 14:51:12 -040010 fn string_length(_: &CxxString) -> usize;
11}
12
13/// Binding to C++ `std::string`.
14///
15/// # Invariants
16///
17/// As an invariant of this API and the static analysis of the cxx::bridge
18/// macro, in Rust code we can never obtain a `CxxString` by value. C++'s string
19/// requires a move constructor and may hold internal pointers, which is not
20/// compatible with Rust's move behavior. Instead in Rust code we will only ever
21/// look at a CxxString through a reference or smart pointer, as in `&CxxString`
22/// or `UniquePtr<CxxString>`.
23#[repr(C)]
24pub struct CxxString {
25 _private: [u8; 0],
26}
27
28impl CxxString {
29 /// Returns the length of the string in bytes.
30 ///
31 /// Matches the behavior of C++ [std::string::size][size].
32 ///
33 /// [size]: https://en.cppreference.com/w/cpp/string/basic_string/size
34 pub fn len(&self) -> usize {
35 unsafe { string_length(self) }
36 }
37
38 /// Returns true if `self` has a length of zero bytes.
David Tolnayd7b8a6e2020-04-24 16:22:55 -070039 ///
40 /// Matches the behavior of C++ [std::string::empty][empty].
41 ///
42 /// [empty]: https://en.cppreference.com/w/cpp/string/basic_string/empty
David Tolnay7db73692019-10-20 14:51:12 -040043 pub fn is_empty(&self) -> bool {
44 self.len() == 0
45 }
46
47 /// Returns a byte slice of this string's contents.
48 pub fn as_bytes(&self) -> &[u8] {
49 let data = self.as_ptr();
50 let len = self.len();
51 unsafe { slice::from_raw_parts(data, len) }
52 }
53
54 /// Produces a pointer to the first character of the string.
55 ///
56 /// Matches the behavior of C++ [std::string::data][data].
57 ///
58 /// Note that the return type may look like `const char *` but is not a
59 /// `const char *` in the typical C sense, as C++ strings may contain
60 /// internal null bytes. As such, the returned pointer only makes sense as a
61 /// string in combination with the length returned by [`len()`](#len).
62 ///
63 /// [data]: https://en.cppreference.com/w/cpp/string/basic_string/data
64 pub fn as_ptr(&self) -> *const u8 {
65 unsafe { string_data(self) }
66 }
67
68 /// Validates that the C++ string contains UTF-8 data and produces a view of
69 /// it as a Rust &amp;str, otherwise an error.
70 pub fn to_str(&self) -> Result<&str, Utf8Error> {
71 str::from_utf8(self.as_bytes())
72 }
73
74 /// If the contents of the C++ string are valid UTF-8, this function returns
75 /// a view as a Cow::Borrowed &amp;str. Otherwise replaces any invalid UTF-8
76 /// sequences with the U+FFFD [replacement character] and returns a
77 /// Cow::Owned String.
78 ///
79 /// [replacement character]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html
80 pub fn to_string_lossy(&self) -> Cow<str> {
81 String::from_utf8_lossy(self.as_bytes())
82 }
83}
84
85impl Display for CxxString {
86 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
David Tolnayd930a792020-03-25 12:24:40 -070087 Display::fmt(self.to_string_lossy().as_ref(), f)
David Tolnay7db73692019-10-20 14:51:12 -040088 }
89}
90
91impl Debug for CxxString {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
David Tolnayd930a792020-03-25 12:24:40 -070093 Debug::fmt(self.to_string_lossy().as_ref(), f)
David Tolnay7db73692019-10-20 14:51:12 -040094 }
95}
David Tolnay42ebfa22020-03-25 12:26:22 -070096
97impl PartialEq for CxxString {
98 fn eq(&self, other: &CxxString) -> bool {
99 self.as_bytes() == other.as_bytes()
100 }
101}
102
103impl PartialEq<CxxString> for str {
104 fn eq(&self, other: &CxxString) -> bool {
105 self.as_bytes() == other.as_bytes()
106 }
107}
108
109impl PartialEq<str> for CxxString {
110 fn eq(&self, other: &str) -> bool {
111 self.as_bytes() == other.as_bytes()
112 }
113}