core_foundation/
string.rs1pub use core_foundation_sys::string::*;
13
14use crate::base::{CFIndexConvertible, TCFType};
15
16use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull};
17use core_foundation_sys::base::{Boolean, CFIndex, CFRange};
18use std::borrow::Cow;
19use std::ffi::CStr;
20use std::fmt;
21use std::ptr;
22use std::str::{self, FromStr};
23
24declare_TCFType! {
25 CFString, CFStringRef
27}
28impl_TCFType!(CFString, CFStringRef, CFStringGetTypeID);
29
30impl FromStr for CFString {
31 type Err = ();
32
33 #[inline]
35 fn from_str(string: &str) -> Result<CFString, ()> {
36 Ok(CFString::new(string))
37 }
38}
39
40impl<'a> From<&'a str> for CFString {
41 #[inline]
42 fn from(string: &'a str) -> CFString {
43 CFString::new(string)
44 }
45}
46
47impl<'a> From<&'a CFString> for Cow<'a, str> {
48 fn from(cf_str: &'a CFString) -> Cow<'a, str> {
49 unsafe {
50 let c_string = CFStringGetCStringPtr(cf_str.0, kCFStringEncodingUTF8);
52 if !c_string.is_null() {
53 let c_str = CStr::from_ptr(c_string);
54 Cow::Borrowed(str::from_utf8_unchecked(c_str.to_bytes()))
55 } else {
56 let char_len = cf_str.char_len();
57
58 let mut bytes_required: CFIndex = 0;
60 CFStringGetBytes(
61 cf_str.0,
62 CFRange {
63 location: 0,
64 length: char_len,
65 },
66 kCFStringEncodingUTF8,
67 0,
68 false as Boolean,
69 ptr::null_mut(),
70 0,
71 &mut bytes_required,
72 );
73
74 let mut buffer = vec![b'\x00'; bytes_required as usize];
76
77 let mut bytes_used: CFIndex = 0;
78 let chars_written = CFStringGetBytes(
79 cf_str.0,
80 CFRange {
81 location: 0,
82 length: char_len,
83 },
84 kCFStringEncodingUTF8,
85 0,
86 false as Boolean,
87 buffer.as_mut_ptr(),
88 buffer.len().to_CFIndex(),
89 &mut bytes_used,
90 );
91 assert_eq!(chars_written, char_len);
92
93 assert_eq!(bytes_used, buffer.len().to_CFIndex());
96 Cow::Owned(String::from_utf8_unchecked(buffer))
97 }
98 }
99 }
100}
101
102impl fmt::Display for CFString {
103 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
104 fmt.write_str(&Cow::from(self))
105 }
106}
107
108impl fmt::Debug for CFString {
109 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110 write!(f, "\"{}\"", self)
111 }
112}
113
114impl CFString {
115 #[inline]
117 pub fn new(string: &str) -> CFString {
118 unsafe {
119 let string_ref = CFStringCreateWithBytes(
120 kCFAllocatorDefault,
121 string.as_ptr(),
122 string.len().to_CFIndex(),
123 kCFStringEncodingUTF8,
124 false as Boolean,
125 );
126 CFString::wrap_under_create_rule(string_ref)
127 }
128 }
129
130 #[inline]
133 pub fn from_static_string(string: &'static str) -> CFString {
134 unsafe {
135 let string_ref = CFStringCreateWithBytesNoCopy(
136 kCFAllocatorDefault,
137 string.as_ptr(),
138 string.len().to_CFIndex(),
139 kCFStringEncodingUTF8,
140 false as Boolean,
141 kCFAllocatorNull,
142 );
143 TCFType::wrap_under_create_rule(string_ref)
144 }
145 }
146
147 #[inline]
149 pub fn char_len(&self) -> CFIndex {
150 unsafe { CFStringGetLength(self.0) }
151 }
152}
153
154impl<'a> PartialEq<&'a str> for CFString {
155 fn eq(&self, other: &&str) -> bool {
156 unsafe {
157 let temp = CFStringCreateWithBytesNoCopy(
158 kCFAllocatorDefault,
159 other.as_ptr(),
160 other.len().to_CFIndex(),
161 kCFStringEncodingUTF8,
162 false as Boolean,
163 kCFAllocatorNull,
164 );
165 self.eq(&CFString::wrap_under_create_rule(temp))
166 }
167 }
168}
169
170impl<'a> PartialEq<CFString> for &'a str {
171 #[inline]
172 fn eq(&self, other: &CFString) -> bool {
173 other.eq(self)
174 }
175}
176
177impl PartialEq<CFString> for String {
178 #[inline]
179 fn eq(&self, other: &CFString) -> bool {
180 other.eq(&self.as_str())
181 }
182}
183
184impl PartialEq<String> for CFString {
185 #[inline]
186 fn eq(&self, other: &String) -> bool {
187 self.eq(&other.as_str())
188 }
189}
190
191#[test]
192fn str_cmp() {
193 let cfstr = CFString::new("hello");
194 assert_eq!("hello", cfstr);
195 assert_eq!(cfstr, "hello");
196 assert_ne!(cfstr, "wrong");
197 assert_ne!("wrong", cfstr);
198 let hello = String::from("hello");
199 assert_eq!(hello, cfstr);
200 assert_eq!(cfstr, hello);
201}
202
203#[test]
204fn string_and_back() {
205 let original = "The quick brown fox jumped over the slow lazy dog.";
206 let cfstr = CFString::from_static_string(original);
207 let converted = cfstr.to_string();
208 assert_eq!(converted, original);
209}