Skip to main content

mtl_sys/
macros.rs

1//! Macros for selector caching, class lookup, and type definitions.
2
3/// Cached selector lookup.
4///
5/// This macro creates a lazily-initialized static selector that is
6/// registered with the Objective-C runtime on first use.
7///
8/// # Examples
9///
10/// ```ignore
11/// // Simple selector
12/// let s = sel!(init);
13///
14/// // Selector with colons (method with parameters)
15/// let s = sel!(initWithFrame:);
16///
17/// // Multiple parameter selector
18/// let s = sel!(newBufferWithLength:options:);
19/// ```
20#[macro_export]
21macro_rules! sel {
22    // Single identifier without colon
23    ($name:ident) => {{
24        static SEL: std::sync::OnceLock<$crate::Sel> = std::sync::OnceLock::new();
25        *SEL.get_or_init(|| $crate::Sel::register(stringify!($name)))
26    }};
27    // Selector with one or more colons (e.g., newBufferWithLength:options:)
28    ($($name:ident :)+) => {{
29        static SEL: std::sync::OnceLock<$crate::Sel> = std::sync::OnceLock::new();
30        *SEL.get_or_init(|| $crate::Sel::register(concat!($(stringify!($name), ":"),+)))
31    }};
32}
33
34/// Cached class lookup.
35///
36/// This macro creates a lazily-initialized static class reference that is
37/// looked up from the Objective-C runtime on first use.
38///
39/// # Panics
40///
41/// Panics if the class is not found in the runtime.
42///
43/// # Examples
44///
45/// ```ignore
46/// let cls = class!(NSObject);
47/// let cls = class!(MTLDevice);
48/// ```
49#[macro_export]
50macro_rules! class {
51    ($name:ident) => {{
52        static CLS: std::sync::OnceLock<$crate::Class> = std::sync::OnceLock::new();
53        *CLS.get_or_init(|| {
54            $crate::Class::get(stringify!($name)).expect(concat!(
55                "class ",
56                stringify!($name),
57                " not found"
58            ))
59        })
60    }};
61}
62
63/// Define a Metal enum type.
64///
65/// Creates a newtype wrapper around a primitive integer type with
66/// associated constants for each variant.
67///
68/// # Examples
69///
70/// ```ignore
71/// metal_enum! {
72///     /// Pixel format for textures.
73///     pub enum PixelFormat: u64 {
74///         Invalid = 0,
75///         A8Unorm = 1,
76///         R8Unorm = 10,
77///     }
78/// }
79/// ```
80#[macro_export]
81macro_rules! metal_enum {
82    (
83        $(#[$meta:meta])*
84        $vis:vis enum $name:ident : $repr:ty {
85            $(
86                $(#[$variant_meta:meta])*
87                $variant:ident = $value:expr
88            ),* $(,)?
89        }
90    ) => {
91        $(#[$meta])*
92        #[repr(transparent)]
93        #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
94        $vis struct $name(pub $repr);
95
96        impl $name {
97            $(
98                $(#[$variant_meta])*
99                pub const $variant: Self = Self($value);
100            )*
101
102            /// Returns the raw integer value.
103            #[inline]
104            pub const fn raw(&self) -> $repr {
105                self.0
106            }
107
108            /// Creates from a raw integer value.
109            #[inline]
110            pub const fn from_raw(value: $repr) -> Self {
111                Self(value)
112            }
113        }
114
115        impl From<$repr> for $name {
116            #[inline]
117            fn from(value: $repr) -> Self {
118                Self(value)
119            }
120        }
121
122        impl From<$name> for $repr {
123            #[inline]
124            fn from(value: $name) -> Self {
125                value.0
126            }
127        }
128    };
129}
130
131/// Define a Metal options/bitflags type.
132///
133/// Creates a newtype wrapper around a primitive integer type with
134/// bitwise operations and associated constants for each flag.
135///
136/// # Examples
137///
138/// ```ignore
139/// metal_options! {
140///     /// Resource storage and caching options.
141///     pub struct ResourceOptions: u64 {
142///         const CPU_CACHE_MODE_DEFAULT = 0;
143///         const CPU_CACHE_MODE_WRITE_COMBINED = 1 << 0;
144///         const STORAGE_MODE_SHARED = 0 << 4;
145///         const STORAGE_MODE_MANAGED = 1 << 4;
146///         const STORAGE_MODE_PRIVATE = 2 << 4;
147///     }
148/// }
149///
150/// let opts = ResourceOptions::STORAGE_MODE_PRIVATE | ResourceOptions::CPU_CACHE_MODE_WRITE_COMBINED;
151/// ```
152#[macro_export]
153macro_rules! metal_options {
154    (
155        $(#[$meta:meta])*
156        $vis:vis struct $name:ident : $repr:ty {
157            $(
158                $(#[$flag_meta:meta])*
159                const $flag:ident = $value:expr;
160            )*
161        }
162    ) => {
163        $(#[$meta])*
164        #[repr(transparent)]
165        #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
166        $vis struct $name(pub $repr);
167
168        impl $name {
169            /// Empty set of flags.
170            pub const NONE: Self = Self(0);
171
172            $(
173                $(#[$flag_meta])*
174                pub const $flag: Self = Self($value);
175            )*
176
177            /// Returns the raw bits.
178            #[inline]
179            pub const fn bits(&self) -> $repr {
180                self.0
181            }
182
183            /// Creates from raw bits.
184            #[inline]
185            pub const fn from_bits(bits: $repr) -> Self {
186                Self(bits)
187            }
188
189            /// Check if empty.
190            #[inline]
191            pub const fn is_empty(&self) -> bool {
192                self.0 == 0
193            }
194
195            /// Check if contains all flags in `other`.
196            #[inline]
197            pub const fn contains(&self, other: Self) -> bool {
198                (self.0 & other.0) == other.0
199            }
200
201            /// Check if contains any flags in `other`.
202            #[inline]
203            pub const fn intersects(&self, other: Self) -> bool {
204                (self.0 & other.0) != 0
205            }
206
207            /// Insert flags.
208            #[inline]
209            pub fn insert(&mut self, other: Self) {
210                self.0 |= other.0;
211            }
212
213            /// Remove flags.
214            #[inline]
215            pub fn remove(&mut self, other: Self) {
216                self.0 &= !other.0;
217            }
218
219            /// Toggle flags.
220            #[inline]
221            pub fn toggle(&mut self, other: Self) {
222                self.0 ^= other.0;
223            }
224
225            /// Set flags based on a boolean.
226            #[inline]
227            pub fn set(&mut self, other: Self, value: bool) {
228                if value {
229                    self.insert(other);
230                } else {
231                    self.remove(other);
232                }
233            }
234        }
235
236        impl std::ops::BitOr for $name {
237            type Output = Self;
238            #[inline]
239            fn bitor(self, rhs: Self) -> Self {
240                Self(self.0 | rhs.0)
241            }
242        }
243
244        impl std::ops::BitOrAssign for $name {
245            #[inline]
246            fn bitor_assign(&mut self, rhs: Self) {
247                self.0 |= rhs.0;
248            }
249        }
250
251        impl std::ops::BitAnd for $name {
252            type Output = Self;
253            #[inline]
254            fn bitand(self, rhs: Self) -> Self {
255                Self(self.0 & rhs.0)
256            }
257        }
258
259        impl std::ops::BitAndAssign for $name {
260            #[inline]
261            fn bitand_assign(&mut self, rhs: Self) {
262                self.0 &= rhs.0;
263            }
264        }
265
266        impl std::ops::BitXor for $name {
267            type Output = Self;
268            #[inline]
269            fn bitxor(self, rhs: Self) -> Self {
270                Self(self.0 ^ rhs.0)
271            }
272        }
273
274        impl std::ops::BitXorAssign for $name {
275            #[inline]
276            fn bitxor_assign(&mut self, rhs: Self) {
277                self.0 ^= rhs.0;
278            }
279        }
280
281        impl std::ops::Not for $name {
282            type Output = Self;
283            #[inline]
284            fn not(self) -> Self {
285                Self(!self.0)
286            }
287        }
288
289        impl std::ops::Sub for $name {
290            type Output = Self;
291            /// Difference: `self & !rhs`
292            #[inline]
293            fn sub(self, rhs: Self) -> Self {
294                Self(self.0 & !rhs.0)
295            }
296        }
297
298        impl std::ops::SubAssign for $name {
299            #[inline]
300            fn sub_assign(&mut self, rhs: Self) {
301                self.0 &= !rhs.0;
302            }
303        }
304
305        impl From<$repr> for $name {
306            #[inline]
307            fn from(value: $repr) -> Self {
308                Self(value)
309            }
310        }
311
312        impl From<$name> for $repr {
313            #[inline]
314            fn from(value: $name) -> Self {
315                value.0
316            }
317        }
318    };
319}
320
321#[cfg(test)]
322mod tests {
323    #[test]
324    fn test_metal_enum() {
325        metal_enum! {
326            pub enum TestEnum: u32 {
327                Zero = 0,
328                One = 1,
329                Two = 2,
330            }
331        }
332
333        assert_eq!(TestEnum::Zero.raw(), 0);
334        assert_eq!(TestEnum::One.raw(), 1);
335        assert_eq!(TestEnum::Two.raw(), 2);
336        assert_eq!(TestEnum::from_raw(1), TestEnum::One);
337    }
338
339    #[test]
340    fn test_metal_options() {
341        metal_options! {
342            pub struct TestFlags: u32 {
343                const A = 1 << 0;
344                const B = 1 << 1;
345                const C = 1 << 2;
346            }
347        }
348
349        let flags = TestFlags::A | TestFlags::B;
350        assert!(flags.contains(TestFlags::A));
351        assert!(flags.contains(TestFlags::B));
352        assert!(!flags.contains(TestFlags::C));
353        assert!(flags.intersects(TestFlags::A));
354        assert!(!flags.intersects(TestFlags::C));
355        assert_eq!(flags.bits(), 0b11);
356
357        let mut flags2 = TestFlags::NONE;
358        flags2.insert(TestFlags::C);
359        assert!(flags2.contains(TestFlags::C));
360        flags2.remove(TestFlags::C);
361        assert!(flags2.is_empty());
362    }
363}