objc2_foundation/uuid.rs
1use core::fmt;
2use core::panic::{RefUnwindSafe, UnwindSafe};
3
4use objc2::encode::{Encode, Encoding, RefEncode};
5use objc2::rc::{Allocated, Retained};
6use objc2::runtime::NSObject;
7use objc2::{extern_methods, msg_send, AnyThread};
8
9use crate::{util, NSUUID};
10
11impl UnwindSafe for NSUUID {}
12impl RefUnwindSafe for NSUUID {}
13
14#[repr(transparent)]
15struct UuidBytes([u8; 16]);
16
17unsafe impl RefEncode for UuidBytes {
18 // Encoding actually depends on the instance class, `__NSConcreteUUID` has
19 // a different method encoding than `NSUUID`, the former takes a `char*`.
20 //
21 // This may also depend on the Foundation / OS version.
22 const ENCODING_REF: Encoding = Encoding::Array(16, &u8::ENCODING);
23}
24
25impl NSUUID {
26 extern_methods!(
27 #[unsafe(method(initWithUUIDBytes:))]
28 fn initWithUUIDBytes(this: Allocated<Self>, bytes: &UuidBytes) -> Retained<Self>;
29
30 #[unsafe(method(getUUIDBytes:))]
31 fn getUUIDBytes(&self, bytes: &mut UuidBytes);
32 );
33}
34
35impl NSUUID {
36 /// The 'nil UUID'.
37 pub fn nil() -> Retained<Self> {
38 Self::from_bytes([0; 16])
39 }
40
41 /// Create a new `NSUUID` from the given bytes.
42 ///
43 /// NOTE: The headers describe `initWithUUIDBytes:` as taking `uuid_t`,
44 /// but their actual implementation may use something else.
45 ///
46 /// Thus, to use this method, you must currently disable encoding
47 /// verification using the `"disable-encoding-assertions"` Cargo feature
48 /// in `objc2`.
49 ///
50 ///
51 /// # Example
52 ///
53 /// Create a new `NSUUID` from the `uuid` crate.
54 ///
55 /// ```ignore
56 /// use uuid::Uuid;
57 /// use objc2_foundation::NSUUID;
58 ///
59 /// let uuid: Uuid;
60 /// # uuid = todo!();
61 /// let obj = NSUUID::from_bytes(uuid.into_bytes());
62 /// assert_eq!(obj.as_bytes(), uuid.into_bytes());
63 /// ```
64 pub fn from_bytes(bytes: [u8; 16]) -> Retained<Self> {
65 let bytes = UuidBytes(bytes);
66 Self::initWithUUIDBytes(Self::alloc(), &bytes)
67 }
68
69 #[cfg(feature = "NSString")]
70 pub fn from_string(string: &crate::NSString) -> Option<Retained<Self>> {
71 Self::initWithUUIDString(Self::alloc(), string)
72 }
73
74 /// Convert the UUID to an array.
75 ///
76 /// NOTE: The headers describe `getUUIDBytes:` as taking `uuid_t`, but
77 /// their actual implementation may use something else.
78 ///
79 /// Thus, to use this method, you must currently disable encoding
80 /// verification using the `"disable-encoding-assertions"` Cargo feature
81 /// in `objc2`.
82 pub fn as_bytes(&self) -> [u8; 16] {
83 let mut bytes = UuidBytes([0; 16]);
84 self.getUUIDBytes(&mut bytes);
85 bytes.0
86 }
87}
88
89impl fmt::Display for NSUUID {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 let string: Retained<NSObject> = unsafe { msg_send![self, UUIDString] };
92 // SAFETY: `UUIDString` returns `NSString`.
93 unsafe { util::display_string(&string, f) }
94 }
95}
96
97impl fmt::Debug for NSUUID {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 // The `uuid` crate does `Debug` and `Display` equally, and so do we.
100
101 let string: Retained<NSObject> = unsafe { msg_send![self, UUIDString] };
102 // SAFETY: `UUIDString` returns `NSString`.
103 unsafe { util::display_string(&string, f) }
104 }
105}
106
107// UUID `compare:` is broken for some reason?
108
109// impl PartialOrd for NSUUID {
110// #[inline]
111// fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
112// Some(self.cmp(other))
113// }
114// }
115
116// impl Ord for NSUUID {
117// fn cmp(&self, other: &Self) -> cmp::Ordering {
118// let res: NSComparisonResult = unsafe { msg_send![self, compare: other] };
119// res.into()
120// }
121// }