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// }