Skip to main content

clack_common/utils/
id.rs

1use core::num::NonZeroU32;
2use std::cmp::Ordering;
3use std::fmt::{Debug, Display, Formatter};
4
5/// A standardized CLAP identifier.
6///
7/// Identifiers in CLAP are 32-bit unsigned integers, but where the [`u32::MAX`] value is invalid.
8///
9/// This invalid value is generally used in the CLAP ABI to indicate failure.
10///
11/// This type ensures that the invalid value (i.e. [`u32::MAX`]) is never used in place of an actual
12/// CLAP ID.
13///
14/// # Example
15///
16/// ```
17/// use clack_common::utils::ClapId;
18///
19/// let id: ClapId = ClapId::new(42);
20/// assert_eq!(42, id.get());
21/// ```
22#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
23pub struct ClapId(NonZeroU32);
24
25impl ClapId {
26    /// Creates a new CLAP identifier from its numeric value.
27    ///
28    /// # Panics
29    ///
30    /// This function will panic if `id` is [`u32::MAX`].
31    ///
32    /// For a non-panicking version, see [`ClapId::from_raw`].
33    #[inline]
34    pub const fn new(id: u32) -> Self {
35        match Self::from_raw(id) {
36            None => panic!("Invalid ClapId"),
37            Some(id) => id,
38        }
39    }
40
41    /// Creates a new CLAP identifier from its raw numeric value.
42    ///
43    /// Returns `None` if the given `id` is [`u32::MAX`].
44    ///
45    /// For a panicking version that returns the [`ClapId`] directly, see [`ClapId::new`].
46    #[inline]
47    pub const fn from_raw(raw: u32) -> Option<Self> {
48        match NonZeroU32::new(raw.wrapping_add(1)) {
49            Some(x) => Some(Self(x)),
50            None => None,
51        }
52    }
53
54    /// Gets the underlying numerical value of this ID.
55    ///
56    /// The returned value is guaranteed to never be [`u32::MAX`].
57    #[inline]
58    pub const fn get(self) -> u32 {
59        self.0.get().wrapping_sub(1)
60    }
61
62    /// Takes an optional identifier, and returns the matching raw, C-FFI compatible value.
63    ///
64    /// This returns the underlying numerical value of the ID if present, or [`u32::MAX`] if `None`.
65    #[inline]
66    pub const fn optional_to_raw(value: Option<ClapId>) -> u32 {
67        match value {
68            None => u32::MAX,
69            Some(value) => value.get(),
70        }
71    }
72}
73
74impl Debug for ClapId {
75    #[inline]
76    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
77        f.debug_tuple("ClapId").field(&self.get()).finish()
78    }
79}
80
81impl Display for ClapId {
82    #[inline]
83    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
84        Display::fmt(&self.get(), f)
85    }
86}
87
88impl From<u32> for ClapId {
89    #[inline]
90    fn from(value: u32) -> Self {
91        ClapId::new(value)
92    }
93}
94
95impl From<ClapId> for u32 {
96    #[inline]
97    fn from(value: ClapId) -> Self {
98        value.get()
99    }
100}
101
102impl PartialEq<u32> for ClapId {
103    #[inline]
104    fn eq(&self, other: &u32) -> bool {
105        self.get() == *other
106    }
107}
108
109impl PartialEq<ClapId> for u32 {
110    #[inline]
111    fn eq(&self, other: &ClapId) -> bool {
112        *self == other.get()
113    }
114}
115
116impl PartialOrd<u32> for ClapId {
117    #[inline]
118    fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
119        self.get().partial_cmp(other)
120    }
121}
122
123impl PartialOrd<ClapId> for u32 {
124    #[inline]
125    fn partial_cmp(&self, other: &ClapId) -> Option<Ordering> {
126        self.partial_cmp(&other.get())
127    }
128}
129
130impl PartialEq<Option<ClapId>> for ClapId {
131    #[inline]
132    fn eq(&self, other: &Option<ClapId>) -> bool {
133        match other {
134            None => false,
135            Some(other) => self.0 == other.0,
136        }
137    }
138}
139
140impl PartialEq<ClapId> for Option<ClapId> {
141    #[inline]
142    fn eq(&self, other: &ClapId) -> bool {
143        match self {
144            None => false,
145            Some(s) => s.0 == other.0,
146        }
147    }
148}