1use std::{
2 ffi::{CStr, CString},
3 fmt,
4 marker::PhantomData,
5 string::String,
6};
7
8use libc::{c_char, c_void};
9use objc::{
10 class, msg_send,
11 runtime::{Class, Object},
12 sel, sel_impl,
13};
14use objc_id::{Id, ShareId};
15
16use crate::{
17 foundation::NSComparisonResult,
18 objective_c_runtime::{
19 self, id,
20 macros::interface_impl,
21 nil,
22 traits::{FromId, PNSObject, ToId},
23 },
24 utils::{to_bool, to_optional},
25};
26
27use super::{
28 string::Encoding, Int, NSArray, NSCharacterSet, NSData, NSError, NSLocale, NSMutableString,
29 NSRange, NSStringCompareOptions, NSStringEncodingConversionOptions, NSStringTransform, UInt,
30 NSURL, UTF8_ENCODING,
31};
32
33#[allow(non_camel_case_types)]
35pub type unichar = u16;
36
37#[repr(C)]
39pub struct NSString {
40 pub ptr: ShareId<Object>,
42 marker: PhantomData<()>,
43}
44
45impl NSString {
46 pub fn bytes(&self) -> *const c_char {
48 unsafe {
49 let bytes: *const c_char = msg_send![&*self.ptr, UTF8String];
50 bytes
51 }
52 }
53
54 pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
56 let bytes = self.bytes();
57
58 unsafe {
59 let bytes = CStr::from_ptr(bytes);
60 bytes.to_str()
61 }
62 }
63}
64
65#[interface_impl(NSObject)]
66impl NSString {
67 #[method]
72 pub fn string() -> Self
73 where
74 Self: Sized + FromId,
75 {
76 unsafe { Self::from_id(msg_send![Self::m_class(), string]) }
77 }
78
79 #[method]
81 pub fn init(mut self) -> Self
82 where
83 Self: Sized + FromId,
84 {
85 unsafe { Self::from_id(msg_send![self.m_self(), init]) }
86 }
87
88 #[method]
104 pub fn init_with_bytes_length_encoding(
105 mut self,
106 bytes: *const c_void,
107 len: UInt,
108 encoding: Encoding,
109 ) -> Self
110 where
111 Self: Sized + FromId,
112 {
113 unsafe {
114 Self::from_id(
115 msg_send![self.m_self(), initWithBytes: bytes length: len encoding: encoding],
116 )
117 }
118 }
119
120 #[method]
133 pub fn init_with_bytes_no_copy_length_encoding_free_when_done(
134 mut self,
135 bytes: *mut c_void,
136 len: UInt,
137 encoding: Encoding,
138 free_buffer: bool,
139 ) -> Self
140 where
141 Self: Sized + FromId,
142 {
143 unsafe {
144 Self::from_id(msg_send![
145 self.m_self(),
146 initWithBytesNoCopy: bytes length: len encoding: encoding freeWhenDone: free_buffer
147 ])
148 }
149 }
150
151 #[method]
163 pub fn init_with_characters_length(mut self, characters: *const unichar, len: UInt) -> Self
164 where
165 Self: Sized + FromId,
166 {
167 unsafe {
168 Self::from_id(msg_send![self.m_self(), initWithCharacters: characters length: len])
169 }
170 }
171
172 #[method]
185 pub fn init_with_characters_no_copy_length_free_when_done(
186 mut self,
187 characters: unichar,
188 length: UInt,
189 free_buffer: bool,
190 ) -> Self
191 where
192 Self: Sized + FromId,
193 {
194 unsafe {
195 Self::from_id(msg_send![
196 self.m_self(),
197 initWithCharactersNoCopy: characters length: length freeWhenDone: free_buffer
198 ])
199 }
200 }
201
202 #[method]
212 pub fn init_with_string<S>(mut self, s: S) -> Self
213 where
214 Self: Sized + FromId,
215 S: INSString,
216 {
217 unsafe { Self::from_id(msg_send![self.m_self(), initWithString: s]) }
218 }
219 #[method]
232 pub fn init_with_cstring_encoding(mut self, c_str: *const c_char, encoding: Encoding) -> Self
233 where
234 Self: Sized + FromId,
235 {
236 unsafe {
237 Self::from_id(msg_send![self.m_self(), initWithCString: c_str encoding: encoding])
238 }
239 }
240
241 #[method]
253 pub fn init_with_utf8_string(mut self, c_str: *const c_char) -> Self
254 where
255 Self: Sized + FromId,
256 {
257 unsafe { Self::from_id(msg_send![self.m_self(), initWithUTF8String: c_str]) }
258 }
259
260 #[method]
272 pub fn init_with_data_encoding(mut self, data: NSData, encoding: Encoding) -> Self
273 where
274 Self: Sized + FromId,
275 {
276 unsafe { Self::from_id(msg_send![self.m_self(), initWithData: data encoding: encoding]) }
277 }
278
279 #[method]
286 pub fn localized_user_notification_string_for_key_arguments<T>(
287 key: NSString,
288 arguments: NSArray<T>,
289 ) -> NSString
290 where
291 T: PNSObject,
292 {
293 unsafe {
294 msg_send![Self::m_class(), localizedUserNotificationStringForKey:key arguments:arguments]
295 }
296 }
297
298 #[method]
310 pub fn string_with_characters_length(characters: *const unichar, length: UInt) -> Self
311 where
312 Self: Sized + FromId,
313 {
314 unsafe {
315 Self::from_id(msg_send![Self::m_class(), stringWithCharacters:characters length:length])
316 }
317 }
318
319 #[method]
329 pub fn string_with_string(s: NSString) -> Self
330 where
331 Self: Sized + 'static + FromId,
332 {
333 unsafe { msg_send![Self::m_class(), stringWithString: s] }
334 }
335
336 #[method]
348 pub fn string_with_cstring_encoding(c_str: *const c_char, encoding: Encoding) -> Self
349 where
350 Self: Sized + FromId,
351 {
352 unsafe {
353 Self::from_id(msg_send![Self::m_class(), stringWithCString:c_str encoding:encoding])
354 }
355 }
356
357 #[method]
368 pub fn string_with_utf8_string(c_str: *const c_char) -> Self
369 where
370 Self: Sized + FromId,
371 {
372 unsafe { Self::from_id(msg_send![Self::m_class(), stringWithUTF8String: c_str]) }
373 }
374
375 #[method]
380 pub fn string_with_contents_of_file_encoding(
381 path: &NSString,
382 enc: Encoding,
383 ) -> Result<Self, NSError>
384 where
385 Self: Sized + FromId,
386 {
387 let mut error = NSError::m_alloc();
388
389 let result = unsafe {
390 Self::from_id(
391 msg_send![Self::m_class(), stringWithContentsOfFile: path.m_self() encoding: enc error: &mut error],
392 )
393 };
394
395 if error.m_self() != nil {
396 Err(error)
397 } else {
398 Ok(result)
399 }
400 }
401
402 #[method]
404 pub fn init_with_contents_of_file_encoding(
405 &mut self,
406 path: &NSString,
407 enc: Encoding,
408 ) -> Result<Self, NSError>
409 where
410 Self: Sized + FromId,
411 {
412 let mut error = NSError::m_alloc();
413
414 let result = unsafe {
415 Self::from_id(
416 msg_send![self.m_self(), initWithContentsOfFile: path.m_self() encoding: enc error: &mut error],
417 )
418 };
419
420 if error.m_self() != nil {
421 Err(error)
422 } else {
423 Ok(result)
424 }
425 }
426
427 #[method]
429 pub fn string_with_contents_of_file_used_encoding(
430 path: &NSString,
431 enc: Encoding,
432 ) -> Result<Self, NSError>
433 where
434 Self: Sized + FromId,
435 {
436 let mut error = NSError::m_alloc();
437
438 let result = unsafe {
439 Self::from_id(
440 msg_send![Self::m_class(), stringWithContentsOfFile: path.m_self() usedEncoding: enc error: &mut error],
441 )
442 };
443
444 if error.m_self() != nil {
445 Err(error)
446 } else {
447 Ok(result)
448 }
449 }
450
451 #[method]
453 pub fn init_with_contents_of_file_used_encoding(
454 &mut self,
455 path: &NSString,
456 enc: Encoding,
457 ) -> Result<Self, NSError>
458 where
459 Self: Sized + FromId,
460 {
461 let mut error = NSError::m_alloc();
462
463 let result = unsafe {
464 Self::from_id(
465 msg_send![self.m_self(), initWithContentsOfFile: path.m_self() usedEncoding: enc error: &mut error],
466 )
467 };
468
469 if error.m_self() != nil {
470 Err(error)
471 } else {
472 Ok(result)
473 }
474 }
475
476 #[method]
481 pub fn string_with_contents_of_url_encoding(
482 path: &NSURL,
483 enc: Encoding,
484 ) -> Result<Self, NSError>
485 where
486 Self: Sized + FromId,
487 {
488 let mut error = NSError::m_alloc();
489
490 let result = unsafe {
491 Self::from_id(
492 msg_send![Self::m_class(), stringWithContentsOfURL: path.m_self() encoding: enc error: &mut error],
493 )
494 };
495
496 if error.m_self() != nil {
497 Err(error)
498 } else {
499 Ok(result)
500 }
501 }
502
503 #[method]
505 pub fn init_with_contents_of_url_encoding(
506 &mut self,
507 path: &NSURL,
508 enc: Encoding,
509 ) -> Result<Self, NSError>
510 where
511 Self: Sized + FromId,
512 {
513 let mut error = NSError::m_alloc();
514
515 let result = unsafe {
516 Self::from_id(
517 msg_send![self.m_self(), initWithContentsOfURL: path.m_self() encoding: enc error: &mut error],
518 )
519 };
520
521 if error.m_self() != nil {
522 Err(error)
523 } else {
524 Ok(result)
525 }
526 }
527
528 #[method]
530 pub fn string_with_contents_of_url_used_encoding(
531 path: &NSURL,
532 enc: Encoding,
533 ) -> Result<Self, NSError>
534 where
535 Self: Sized + FromId,
536 {
537 let mut error = NSError::m_alloc();
538
539 let result = unsafe {
540 Self::from_id(
541 msg_send![Self::m_class(), stringWithContentsOfURL: path.m_self() usedEncoding: enc error: &mut error],
542 )
543 };
544
545 if error.m_self() != nil {
546 Err(error)
547 } else {
548 Ok(result)
549 }
550 }
551
552 #[method]
554 pub fn init_with_contents_of_url_used_encoding(
555 &mut self,
556 path: &NSURL,
557 enc: Encoding,
558 ) -> Result<Self, NSError>
559 where
560 Self: Sized + FromId,
561 {
562 let mut error = NSError::m_alloc();
563
564 let result = unsafe {
565 Self::from_id(
566 msg_send![self.m_self(), initWithContentsOfURL: path.m_self() usedEncoding: enc error: &mut error],
567 )
568 };
569
570 if error.m_self() != nil {
571 Err(error)
572 } else {
573 Ok(result)
574 }
575 }
576
577 #[property]
582 pub fn length(&self) -> UInt {
583 unsafe { msg_send![self.m_self(), length] }
584 }
585
586 #[method]
600 pub fn length_of_bytes_using_encoding(&self, enc: Encoding) -> UInt {
601 unsafe { msg_send![self.m_self(), lengthOfBytesUsingEncoding: enc] }
602 }
603
604 #[method]
617 pub fn maximum_length_of_bytes_using_encoding(&self, enc: Encoding) -> Int {
618 unsafe { msg_send![self.m_self(), maximumLengthOfBytesUsingEncoding: enc] }
619 }
620
621 #[method]
634 pub fn character_at_index(&self, index: UInt) -> char {
635 unsafe { msg_send![self.m_self(), characterAtIndex: index] }
636 }
637
638 #[method]
645 pub fn get_characters_range(&self, buffer: *mut unichar, range: NSRange) {
646 unsafe { msg_send![self.m_self(), getCharacters: buffer range: range] }
647 }
648
649 #[method]
661 #[allow(clippy::too_many_arguments)]
662 pub fn get_bytes_max_length_used_length_encoding_options_range_remaining_range(
663 &self,
664 buffer: *mut c_void,
665 max_length: Int,
666 used_length: *mut Int,
667 encoding: Encoding,
668 options: NSStringEncodingConversionOptions,
669 range: NSRange,
670 remaining_range: NSRange,
671 ) -> bool {
672 unsafe {
673 to_bool(
674 msg_send![self.m_self(), getBytes: buffer maxLength: max_length usedLength: used_length encoding: encoding options: options range: range remainingRange: remaining_range],
675 )
676 }
677 }
678
679 #[method]
692 pub fn c_string_using_encoding(&self, encoding: Encoding) -> *const c_char {
693 unsafe { msg_send![self.m_self(), cStringUsingEncoding: encoding] }
694 }
695
696 #[method]
708 pub fn get_cstring_max_length_encoding(
709 &self,
710 buffer: *mut c_char,
711 max_length: UInt,
712 encoding: Encoding,
713 ) -> bool {
714 unsafe {
715 to_bool(
716 msg_send![self.m_self(), getCString: buffer maxLength: max_length encoding: encoding],
717 )
718 }
719 }
720
721 #[property]
723 pub fn utf8_string(&self) -> *const c_char {
724 unsafe { msg_send![self.m_self(), UTF8String] }
725 }
726
727 #[method]
743 pub fn case_insensitive_compare<S>(&self, string: S) -> NSComparisonResult
744 where
745 S: INSString,
746 {
747 unsafe { msg_send![self.m_self(), caseInsensitiveCompare: string] }
748 }
749
750 #[method]
763 pub fn localized_case_insensitive_compare<S>(&self, string: S) -> NSComparisonResult
764 where
765 S: INSString,
766 {
767 unsafe { msg_send![self.m_self(), localizedCaseInsensitiveCompare: string] }
768 }
769
770 #[method]
789 pub fn compare<S>(&self, string: S) -> NSComparisonResult
790 where
791 S: INSString,
792 {
793 unsafe { msg_send![self.m_self(), compare: string] }
794 }
795
796 #[method]
808 pub fn localized_compare<S>(&self, string: S) -> NSComparisonResult
809 where
810 S: INSString,
811 {
812 unsafe { msg_send![self.m_self(), localizedCompare: string] }
813 }
814
815 #[method]
826 pub fn compare_options<S>(&self, string: S, mask: NSStringCompareOptions) -> NSComparisonResult
827 where
828 S: INSString,
829 {
830 unsafe { msg_send![self.m_self(), compare: string options: mask] }
831 }
832
833 #[method]
841 pub fn compare_options_range<S>(
842 &self,
843 string: S,
844 mask: NSStringCompareOptions,
845 range: NSRange,
846 ) -> NSComparisonResult
847 where
848 S: INSString,
849 {
850 unsafe { msg_send![self.m_self(), compare: string options: mask range: range] }
851 }
852
853 #[method]
866 pub fn compare_options_range_locale<S>(
867 &self,
868 string: S,
869 mask: NSStringCompareOptions,
870 range: NSRange,
871 locale: NSLocale,
872 ) -> NSComparisonResult
873 where
874 S: INSString,
875 {
876 unsafe {
877 msg_send![self.m_self(), compare: string options: mask range: range locale: locale]
878 }
879 }
880
881 #[method]
891 pub fn localized_standard_compare<S>(&self, string: S) -> NSComparisonResult
892 where
893 S: INSString,
894 {
895 unsafe { msg_send![self.m_self(), localizedStandardCompare: string] }
896 }
897
898 #[method]
908 pub fn has_prefix<S>(&self, prefix: S) -> bool
909 where
910 S: INSString,
911 {
912 unsafe { to_bool(msg_send![self.m_self(), hasPrefix: prefix]) }
913 }
914
915 #[method]
925 pub fn has_suffix<S>(&self, suffix: S) -> bool
926 where
927 S: INSString,
928 {
929 unsafe { to_bool(msg_send![self.m_self(), hasSuffix: suffix]) }
930 }
931
932 #[method]
942 pub fn is_equal_to_string<S>(&self, string: S) -> bool
943 where
944 S: INSString,
945 {
946 unsafe { to_bool(msg_send![self.m_self(), isEqualToString: string]) }
947 }
948
949 #[method]
958 pub fn string_by_appending_string<S>(&self, string: S) -> NSString
959 where
960 S: INSString,
961 {
962 unsafe { msg_send![self.m_self(), stringByAppendingString: string] }
963 }
964
965 #[method]
977 pub fn string_by_padding_to_length_with_string_starting_at_index<S>(
978 &self,
979 new_length: UInt,
980 pad_string: S,
981 starting_at: UInt,
982 ) -> NSString
983 where
984 S: INSString,
985 {
986 unsafe {
987 NSString::from_id(
988 msg_send![self.m_self(), stringByPaddingToLength: new_length withString: pad_string startingAtIndex: starting_at],
989 )
990 }
991 }
992
993 #[property]
998 pub fn lowercase_string(&self) -> NSString {
999 unsafe { NSString::from_id(msg_send![self.m_self(), lowercaseString]) }
1000 }
1001
1002 #[property]
1005 pub fn localized_lowercase_string(&self) -> NSString {
1006 unsafe { NSString::from_id(msg_send![self.m_self(), localizedLowercaseString]) }
1007 }
1008
1009 #[method]
1020 pub fn lowercase_string_with_locale(&self, locale: NSLocale) -> NSString {
1021 unsafe { NSString::from_id(msg_send![self.m_self(), lowercaseStringWithLocale: locale]) }
1022 }
1023
1024 #[property]
1026 pub fn uppercase_string(&self) -> NSString {
1027 unsafe { NSString::from_id(msg_send![self.m_self(), uppercaseString]) }
1028 }
1029
1030 #[property]
1033 pub fn localized_uppercase_string(&self) -> NSString {
1034 unsafe { NSString::from_id(msg_send![self.m_self(), localizedUppercaseString]) }
1035 }
1036
1037 #[method]
1048 pub fn uppercase_string_with_locale(&self, locale: NSLocale) -> NSString {
1049 unsafe { NSString::from_id(msg_send![self.m_self(), uppercaseStringWithLocale: locale]) }
1050 }
1051
1052 #[property]
1054 pub fn capitalized_string(&self) -> NSString {
1055 unsafe { NSString::from_id(msg_send![self.m_self(), capitalizedString]) }
1056 }
1057
1058 #[property]
1061 pub fn localized_capitalized_string(&self) -> NSString {
1062 unsafe { NSString::from_id(msg_send![self.m_self(), localizedCapitalizedString]) }
1063 }
1064
1065 #[method]
1068 pub fn capitalized_string_with_locale(&self, locale: NSLocale) -> NSString {
1069 unsafe {
1070 NSString::from_id(msg_send![
1071 self.m_self(),
1072 capitalizedStringWithLocale: locale
1073 ])
1074 }
1075 }
1076
1077 #[method]
1082 pub fn components_separated_by_string<S>(&self, separator: S) -> NSArray<NSString>
1083 where
1084 S: INSString,
1085 {
1086 unsafe {
1087 NSArray::from_id(msg_send![
1088 self.m_self(),
1089 componentsSeparatedByString: separator
1090 ])
1091 }
1092 }
1093
1094 #[method]
1096 pub fn components_separated_by_characters_in_set(
1097 &self,
1098 separator: &NSCharacterSet,
1099 ) -> NSArray<NSString> {
1100 unsafe {
1101 NSArray::from_id(msg_send![
1102 self.m_self(),
1103 componentsSeparatedByCharactersInSet: separator.m_self()
1104 ])
1105 }
1106 }
1107
1108 #[method]
1110 pub fn string_by_trimming_characters_in_set(&self, set: &NSCharacterSet) -> NSString {
1111 unsafe {
1112 NSString::from_id(msg_send![
1113 self.m_self(),
1114 stringByTrimmingCharactersInSet: set.m_self()
1115 ])
1116 }
1117 }
1118
1119 #[method]
1121 pub fn substring_from_index(&self, from: UInt) -> NSString {
1122 unsafe { NSString::from_id(msg_send![self.m_self(), substringFromIndex: from]) }
1123 }
1124
1125 #[method]
1127 pub fn substring_with_range(&self, range: NSRange) -> NSString {
1128 unsafe { NSString::from_id(msg_send![self.m_self(), substringWithRange: range]) }
1129 }
1130
1131 #[method]
1133 pub fn substring_to_index(&self, to: UInt) -> NSString {
1134 unsafe { NSString::from_id(msg_send![self.m_self(), substringToIndex: to]) }
1135 }
1136
1137 #[property]
1142 pub fn decomposed_string_with_canonical_mapping(&self) -> NSString {
1143 unsafe {
1144 NSString::from_id(msg_send![
1145 self.m_self(),
1146 decomposedStringWithCanonicalMapping
1147 ])
1148 }
1149 }
1150
1151 #[property]
1153 pub fn decomposed_string_with_compatibility_mapping(&self) -> NSString {
1154 unsafe {
1155 NSString::from_id(msg_send![
1156 self.m_self(),
1157 decomposedStringWithCompatibilityMapping
1158 ])
1159 }
1160 }
1161
1162 #[property]
1164 pub fn precomposed_string_with_canonical_mapping(&self) -> NSString {
1165 unsafe {
1166 NSString::from_id(msg_send![
1167 self.m_self(),
1168 precomposedStringWithCanonicalMapping
1169 ])
1170 }
1171 }
1172
1173 #[property]
1175 pub fn precomposed_string_with_compatibility_mapping(&self) -> NSString {
1176 unsafe {
1177 NSString::from_id(msg_send![
1178 self.m_self(),
1179 precomposedStringWithCompatibilityMapping
1180 ])
1181 }
1182 }
1183
1184 #[method]
1189 pub fn string_by_folding_with_options_locale(
1190 &mut self,
1191 options: NSStringCompareOptions,
1192 locale: &NSLocale,
1193 ) -> NSString {
1194 unsafe {
1195 NSString::from_id(
1196 msg_send![self.m_self(), stringByFoldingWithOptions: options locale: locale.m_self() ],
1197 )
1198 }
1199 }
1200
1201 #[method]
1211 pub fn string_by_applying_transform_reverse(
1212 &mut self,
1213 transform: NSStringTransform,
1214 reverse: bool,
1215 ) -> Option<NSString> {
1216 unsafe {
1217 to_optional(
1218 msg_send![self.m_self(), stringByApplyingTransform: transform reverse: reverse],
1219 )
1220 }
1221 }
1222
1223 #[method]
1236 pub fn contains_string<S>(&self, other: S) -> bool
1237 where
1238 S: INSString,
1239 {
1240 unsafe { to_bool(msg_send![self.m_self(), containsString: other]) }
1241 }
1242
1243 #[property]
1248 pub fn available_string_encodings() -> *const Encoding {
1249 unsafe { msg_send![Self::m_class(), availableStringEncodings] }
1250 }
1251
1252 #[property]
1254 pub fn default_cstring_encoding() -> Encoding {
1255 unsafe { msg_send![Self::m_class(), defaultCStringEncoding] }
1256 }
1257
1258 #[method]
1260 pub fn can_be_converted_to_encoding(&self, encoding: Encoding) -> bool {
1261 unsafe { to_bool(msg_send![self.m_self(), canBeConvertedToEncoding: encoding]) }
1262 }
1263
1264 #[method]
1266 pub fn data_using_encoding(&self, encoding: Encoding) -> NSData {
1267 unsafe { NSData::from_id(msg_send![self.m_self(), dataUsingEncoding: encoding]) }
1268 }
1269}
1270
1271impl PNSObject for NSString {
1272 fn m_class<'a>() -> &'a Class {
1273 class!(NSString)
1274 }
1275
1276 fn m_self(&self) -> id {
1277 unsafe { msg_send![&*self.ptr, self] }
1278 }
1279}
1280
1281unsafe impl objective_c_runtime::Encode for NSString {
1282 fn encode() -> objc::Encoding {
1283 unsafe { objective_c_runtime::Encoding::from_str("@") }
1284 }
1285}
1286
1287impl Default for NSString {
1288 fn default() -> Self {
1289 Self::string()
1290 }
1291}
1292
1293impl fmt::Debug for NSString {
1294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1295 write!(f, "{}", self.p_description().as_str().unwrap())
1296 }
1297}
1298
1299impl fmt::Display for NSString {
1300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1301 write!(f, "{}", self.p_description().as_str().unwrap())
1302 }
1303}
1304
1305impl Clone for NSString {
1306 fn clone(&self) -> Self {
1307 unsafe { msg_send![&*self.ptr, retain] }
1308 }
1309}
1310
1311impl ToId for NSString {
1312 fn to_id(self) -> id {
1313 unsafe { &mut *self.m_self() }
1314 }
1315}
1316
1317impl FromId for NSString {
1318 unsafe fn from_id(id: id) -> Self {
1319 NSString {
1320 ptr: Id::from_ptr(id),
1321 marker: PhantomData,
1322 }
1323 }
1324}
1325
1326impl From<NSMutableString> for NSString {
1327 fn from(string: NSMutableString) -> Self {
1328 unsafe {
1329 let ptr: id = msg_send![NSString::m_class(), alloc];
1330 let ptr = msg_send![ptr, initWithString: string];
1331 NSString::from_id(ptr)
1332 }
1333 }
1334}
1335
1336impl From<String> for NSString {
1337 fn from(s: String) -> Self {
1339 let c_string = CString::new(s.clone()).unwrap();
1340 NSString {
1341 ptr: unsafe {
1342 let nsstring: id = msg_send![class!(NSString), alloc];
1343 Id::from_ptr(
1344 msg_send![nsstring, initWithBytes:c_string.into_raw() as *mut Object
1345 length:s.len()
1346 encoding:UTF8_ENCODING
1347 ],
1348 )
1349 },
1350
1351 marker: PhantomData,
1352 }
1353 }
1354}
1355
1356impl From<&str> for NSString {
1357 fn from(s: &str) -> Self {
1359 let objc = unsafe {
1360 let nsstring: *mut Object = msg_send![class!(NSString), alloc];
1361 Id::from_ptr(
1362 msg_send![nsstring, initWithBytes: CString::new(s).unwrap().into_raw() as *mut Object
1363 length:s.len()
1364 encoding:UTF8_ENCODING
1365 ],
1366 )
1367 };
1368
1369 NSString {
1370 ptr: objc,
1371 marker: PhantomData,
1372 }
1373 }
1374}
1375
1376impl From<&&str> for NSString {
1377 fn from(s: &&str) -> Self {
1379 let objc = unsafe {
1380 let nsstring: *mut Object = msg_send![class!(NSString), alloc];
1381 Id::from_ptr(
1382 msg_send![nsstring, initWithBytes: CString::new(*s).unwrap().into_raw() as *mut Object
1383 length:s.len()
1384 encoding:UTF8_ENCODING
1385 ],
1386 )
1387 };
1388
1389 NSString {
1390 ptr: objc,
1391 marker: PhantomData,
1392 }
1393 }
1394}
1395
1396impl From<char> for NSString {
1397 fn from(c: char) -> Self {
1399 let objc = unsafe {
1400 let nsstring: *mut Object = msg_send![class!(NSString), alloc];
1401 Id::from_ptr(
1402 msg_send![nsstring, initWithBytes: c.encode_utf8(&mut [0; 4]).as_ptr()
1403 length:1
1404 encoding:UTF8_ENCODING
1405 ],
1406 )
1407 };
1408
1409 NSString {
1410 ptr: objc,
1411 marker: PhantomData,
1412 }
1413 }
1414}
1415
1416impl From<NSString> for &str {
1417 fn from(string: NSString) -> Self {
1419 unsafe {
1420 let ptr: *const c_char = msg_send![string.ptr, UTF8String];
1421 CStr::from_ptr(ptr).to_str().unwrap()
1422 }
1423 }
1424}
1425
1426impl From<(&str, Encoding)> for NSString {
1427 fn from((s, encoding): (&str, Encoding)) -> Self {
1429 let objc = unsafe {
1430 let nsstring: *mut Object = msg_send![class!(NSString), alloc];
1431 Id::from_ptr(msg_send![nsstring, initWithBytes:s.as_ptr()
1432 length:s.len()
1433 encoding:encoding
1434 ])
1435 };
1436
1437 NSString {
1438 ptr: objc,
1439 marker: PhantomData,
1440 }
1441 }
1442}
1443
1444impl PartialEq for NSString {
1445 fn eq(&self, other: &Self) -> bool {
1447 self.localized_compare(other.clone()) == NSComparisonResult::OrderedSame
1448 }
1449}
1450
1451impl Eq for NSString {}
1452
1453impl PartialEq<&str> for NSString {
1454 fn eq(&self, other: &&str) -> bool {
1456 self.as_str().unwrap() == *other
1457 }
1458}
1459
1460#[cfg(test)]
1461mod tests {
1462 use crate::foundation::{
1463 string::Encoding, string_transform::LatinToKatakana, NSComparisonResult,
1464 };
1465
1466 use super::*;
1467
1468 #[test]
1469 fn test_from_str() {
1470 let s = NSString::from("Hello, World!");
1471 assert_eq!(s, "Hello, World!");
1472 }
1473
1474 #[test]
1475 fn test_bytes() {
1476 let s = NSString::from("Hello, World!");
1477 let other = s.bytes();
1478 assert_eq!(s.bytes(), other);
1479 }
1480
1481 #[test]
1482 fn test_bytes_len() {
1483 let s = NSString::from("Hello, World!");
1484 assert_eq!(s.length_of_bytes_using_encoding(Encoding::UTF8), 13);
1485 }
1486
1487 #[test]
1488 fn test_as_str() {
1489 let s = NSString::from("Hello, World!");
1490 assert_eq!(s, "Hello, World!");
1491 }
1492
1493 #[test]
1494 fn test_to_string() {
1495 let s = NSString::from("Hello, World!");
1496 assert_eq!(s.to_string(), "Hello, World!".to_string());
1497 }
1498
1499 #[test]
1500 fn test_length() {
1501 let s = NSString::from("Hello, World!");
1502 assert_eq!(s.length(), 13);
1503 }
1504
1505 #[test]
1506 fn test_contains() {
1507 let s = NSString::from("Hello, World!");
1508 assert!(s.contains_string(NSString::from("Hello")));
1509 assert!(!s.contains_string(NSString::from("Goodbye")));
1510 }
1511
1512 #[test]
1513 fn test_character_at() {
1514 let s = NSString::from("Hello, World!");
1515 assert_eq!(s.character_at_index(0), 'H');
1516 assert_eq!(s.character_at_index(1), 'e');
1517 assert_eq!(s.character_at_index(2), 'l');
1518 assert_eq!(s.character_at_index(3), 'l');
1519 assert_eq!(s.character_at_index(4), 'o');
1520 assert_eq!(s.character_at_index(5), ',');
1521 assert_eq!(s.character_at_index(6), ' ');
1522 assert_eq!(s.character_at_index(7), 'W');
1523 assert_eq!(s.character_at_index(8), 'o');
1524 assert_eq!(s.character_at_index(9), 'r');
1525 assert_eq!(s.character_at_index(10), 'l');
1526 assert_eq!(s.character_at_index(11), 'd');
1527 assert_eq!(s.character_at_index(12), '!');
1528 }
1529
1530 #[test]
1531 fn test_has_prefix() {
1532 let s = NSString::from("Hello, World!");
1533 assert!(s.has_prefix(NSString::from("Hello")));
1534 assert!(!s.has_prefix(NSString::from("Goodbye")));
1535 }
1536
1537 #[test]
1538 fn test_has_suffix() {
1539 let s = NSString::from("Hello, World!");
1540 assert!(s.has_suffix(NSString::from("World!")));
1541 assert!(!s.has_suffix(NSString::from("Goodbye")));
1542 }
1543
1544 #[test]
1545 fn test_is_equal_to() {
1546 let s = NSString::from("Hello, World!");
1547 assert!(s.is_equal_to_string(NSString::from("Hello, World!")));
1548 assert!(!s.is_equal_to_string(NSString::from("Goodbye, World!")));
1549 }
1550
1551 #[test]
1552 fn test_length_of_bytes() {
1553 let s = NSString::from("Hello, World!");
1554 assert_eq!(s.length_of_bytes_using_encoding(Encoding::UTF8), 13);
1555 }
1556
1557 #[test]
1558 fn test_maximum_length_of_bytes() {
1559 let s = NSString::from("Hello, World!");
1560 assert_eq!(s.maximum_length_of_bytes_using_encoding(Encoding::UTF8), 39);
1561 }
1562
1563 #[test]
1564 fn test_case_insensitive_compare() {
1565 let s = NSString::from("Hello, World!");
1566 assert_eq!(
1567 s.case_insensitive_compare(NSString::from("hello, world!")),
1568 NSComparisonResult::OrderedSame
1569 );
1570 assert_eq!(
1571 s.case_insensitive_compare(NSString::from("goodbye, world!")),
1572 NSComparisonResult::OrderedDescending
1573 );
1574 }
1575
1576 #[test]
1577 fn test_compare() {
1578 let s = NSString::from("Hello, World!");
1579 assert_eq!(
1580 s.compare(NSString::from("Hello, World!")),
1581 NSComparisonResult::OrderedSame
1582 );
1583 assert_eq!(
1584 s.compare(NSString::from("Goodbye, World!")),
1585 NSComparisonResult::OrderedDescending
1586 );
1587 }
1588
1589 #[test]
1590 fn test_localized_standard_compare() {
1591 let s = NSString::from("Hello, World!");
1592 assert_eq!(
1593 s.localized_standard_compare(NSString::from("Hello, World!")),
1594 NSComparisonResult::OrderedSame
1595 );
1596 assert_eq!(
1597 s.localized_standard_compare(NSString::from("Goodbye, World!")),
1598 NSComparisonResult::OrderedDescending
1599 );
1600 }
1601
1602 #[test]
1603 fn test_applying_transform() {
1604 let mut s = NSString::from("Katakana!");
1605 let transform = unsafe { LatinToKatakana };
1606 assert_eq!(
1607 s.string_by_applying_transform_reverse(transform, false)
1608 .unwrap(),
1609 "カタカナ!"
1610 );
1611 }
1612
1613 #[test]
1614 fn test_im_case_insensitive_compare() {
1615 let s = NSString::from("Hello, World!");
1616 assert_eq!(
1617 s.case_insensitive_compare(NSString::from("hello, world!")),
1618 NSComparisonResult::OrderedSame
1619 );
1620 assert_eq!(
1621 s.case_insensitive_compare(NSString::from("goodbye, world!")),
1622 NSComparisonResult::OrderedDescending
1623 );
1624 }
1625
1626 #[test]
1627 fn test_string_by_padding_to_length_with_string_starting_at_index() {
1628 let s = NSString::from("Hello, World!");
1629
1630 assert_eq!(
1631 s.string_by_padding_to_length_with_string_starting_at_index(20, NSString::from("."), 0),
1632 "Hello, World!......."
1633 );
1634 }
1635}