Skip to main content

clack_common/utils/
universal_plugin_id.rs

1use clap_sys::universal_plugin_id::clap_universal_plugin_id;
2use core::ffi::CStr;
3
4/// Known ABI types that can be used in [`UniversalPluginId`]s.
5///
6/// This is non-exhaustive, as other plugin ID types may be added in the future.
7#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
8#[non_exhaustive]
9pub enum UniversalPluginAbi {
10    /// The CLAP ABI.
11    Clap,
12    /// The VST3 ABI.
13    VST3,
14    /// The VST2 ABI.
15    VST2,
16    /// The AU ABI.
17    AU,
18}
19
20impl UniversalPluginAbi {
21    /// Returns the ABI string matching the given ABI type.
22    ///
23    /// This constant can then be used in the [`UniversalPluginId::abi`] field.
24    #[inline]
25    pub const fn abi_str(&self) -> &'static CStr {
26        match self {
27            UniversalPluginAbi::Clap => c"clap",
28            UniversalPluginAbi::VST3 => c"vst3",
29            UniversalPluginAbi::VST2 => c"vst2",
30            UniversalPluginAbi::AU => c"au",
31        }
32    }
33
34    /// Returns the ABI type matching the given ABI string.
35    ///
36    /// If the ABI type string doesn't match a known [`UniversalPluginAbi`] variant, this returns
37    /// [`None`].
38    ///
39    /// This comparison is case-sensitive.
40    #[inline]
41    pub const fn from_abi_str(abi: &CStr) -> Option<Self> {
42        match abi.to_bytes() {
43            b"clap" => Some(UniversalPluginAbi::Clap),
44            b"vst3" => Some(UniversalPluginAbi::VST3),
45            b"vst2" => Some(UniversalPluginAbi::VST2),
46            b"au" => Some(UniversalPluginAbi::AU),
47            _ => None,
48        }
49    }
50}
51
52/// A unique identifier that works across plugin APIs.
53///
54/// This is a ([`abi`](Self::abi), [`id`](Self::id)) pair of borrowed strings that live for the `'a`
55/// lifetime.
56#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
57pub struct UniversalPluginId<'a> {
58    /// The ABI string.
59    ///
60    /// It can be e.g. "clap", "vst2", "vst3", "au", etc.
61    ///
62    /// See [`UniversalPluginAbi::abi_str`] for known values this field can take.
63    pub abi: &'a CStr,
64    /// The ID string of the plugin.
65    ///
66    /// This is formatted differently depending on the ABI type:
67    ///
68    /// * **CLAP**: Use the plugin ID (e.g. `"com.u-he.diva"`)
69    /// * **AU**: Format the string as "type:subt:manu" (e.g. `"aumu:SgXT:VmbA"`)
70    /// * **VST2**: Format the ID as a signed 32-bit integer (e.g. `"-4382976"`)
71    /// * **VST3**: Format the ID as a hyphenated UUID (e.g. `"123e4567-e89b-12d3-a456-426614174000"`)
72    pub id: &'a CStr,
73}
74
75impl<'a> UniversalPluginId<'a> {
76    /// Creates a [`UniversalPluginId`] from its raw, C-FFI compatible representation.
77    ///
78    /// If either of the string pointers within the given struct are `NULL`, this returns [`None`].
79    ///
80    /// # Safety
81    ///
82    /// Either of the contained pointers can be `NULL`, in which case the following requirements do
83    /// not apply.
84    ///
85    /// Both pointers must point to valid C strings, and remain valid for the duration of the `'a` lifetime.
86    ///
87    /// See the documentation of [`CStr::from_ptr`] for the exhaustive list of safety requirements.
88    #[inline]
89    pub const unsafe fn from_raw(raw: clap_universal_plugin_id) -> Option<Self> {
90        Some(Self {
91            abi: if raw.abi.is_null() {
92                return None;
93            } else {
94                // SAFETY: pointer is guaranteed to be valid by caller
95                unsafe { CStr::from_ptr(raw.abi) }
96            },
97            id: if raw.id.is_null() {
98                return None;
99            } else {
100                // SAFETY: pointer is guaranteed to be valid by caller
101                unsafe { CStr::from_ptr(raw.id) }
102            },
103        })
104    }
105
106    /// Creates a [`UniversalPluginId`] from a pointer to its raw, C-FFI compatible representation.
107    ///
108    /// If the given `ptr`, or either of the string pointers within the pointed struct are `NULL`, this returns [`None`].
109    ///
110    /// This function is a version of the [`from_raw`](Self::from_raw) method, except it allows to read
111    /// directly from a raw pointer without actually borrowing it, which may be slightly safer.
112    ///
113    /// # Safety
114    ///
115    /// The given `ptr` must be well-aligned and valid for that.
116    /// On top of that, the [`clap_universal_plugin_id`] value it points to must also satisfy the
117    /// safety requirements of the [`from_raw`](Self::from_raw) method.
118    #[inline]
119    pub const unsafe fn from_raw_ptr(ptr: *const clap_universal_plugin_id) -> Option<Self> {
120        if ptr.is_null() {
121            return None;
122        }
123
124        // SAFETY: pointer is guaranteed to be valid for reads by caller
125        let plugin_id = unsafe { ptr.read() };
126        Self::from_raw(plugin_id)
127    }
128
129    /// Creates a [`UniversalPluginId`] for a CLAP plugin with a given `id`.
130    #[inline]
131    pub const fn clap(id: &'a CStr) -> Self {
132        Self { abi: c"clap", id }
133    }
134
135    /// Returns the [`UniversalPluginAbi`] matching this ID's plugin ABI.
136    ///
137    /// If the ABI type string doesn't match a known [`UniversalPluginAbi`] variant, this returns
138    /// [`None`].
139    #[inline]
140    pub const fn abi(&self) -> Option<UniversalPluginAbi> {
141        UniversalPluginAbi::from_abi_str(self.abi)
142    }
143
144    /// Returns a raw, C-FFI compatible representation of this ID.
145    ///
146    /// The pointers contained in this struct are valid for the `'a` lifetime.
147    #[inline]
148    pub const fn to_raw(&self) -> clap_universal_plugin_id {
149        clap_universal_plugin_id {
150            id: self.id.as_ptr(),
151            abi: self.abi.as_ptr(),
152        }
153    }
154}