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}