1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use clap_sys::plugin::clap_plugin_descriptor;
use clap_sys::version::CLAP_VERSION;
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::os::raw::c_char;

use crate::prelude::ClapPlugin;

/// A static descriptor for a plugin. This is used in both the descriptor and on the plugin object
/// itself.
///
/// This cannot be cloned as [`Self::clap_features_ptrs`] contains pointers to
/// [Self::clap_features].
pub struct PluginDescriptor<P: ClapPlugin> {
    // We need [CString]s for all of `ClapPlugin`'s `&str` fields
    clap_id: CString,
    name: CString,
    vendor: CString,
    url: CString,
    version: CString,
    clap_manual_url: Option<CString>,
    clap_support_url: Option<CString>,
    clap_description: Option<CString>,
    clap_features: Vec<CString>,

    /// The contains pointers to the strings in `clap_features`.
    clap_features_ptrs: Vec<*const c_char>,
    /// We only support a single plugin per descriptor right now, so we'll fill in the plugin
    /// descriptor upfront. We also need to initialize the `CString` fields above first before we
    /// can initialize this plugin descriptor.
    plugin_descriptor: Option<clap_plugin_descriptor>,

    /// The plugin's type.
    _phantom: PhantomData<P>,
}

impl<P: ClapPlugin> Default for PluginDescriptor<P> {
    fn default() -> Self {
        let mut descriptor = Self {
            clap_id: CString::new(P::CLAP_ID).expect("`CLAP_ID` contained null bytes"),
            name: CString::new(P::NAME).expect("`NAME` contained null bytes"),
            vendor: CString::new(P::VENDOR).expect("`VENDOR` contained null bytes"),
            url: CString::new(P::URL).expect("`URL` contained null bytes"),
            version: CString::new(P::VERSION).expect("`VERSION` contained null bytes"),
            clap_manual_url: P::CLAP_MANUAL_URL
                .map(|url| CString::new(url).expect("`CLAP_MANUAL_URL` contained null bytes")),
            clap_support_url: P::CLAP_SUPPORT_URL
                .map(|url| CString::new(url).expect("`CLAP_SUPPORT_URL` contained null bytes")),
            clap_description: P::CLAP_DESCRIPTION.map(|description| {
                CString::new(description).expect("`CLAP_DESCRIPTION` contained null bytes")
            }),
            clap_features: P::CLAP_FEATURES
                .iter()
                .map(|feat| feat.as_str())
                .map(|s| CString::new(s).expect("`CLAP_FEATURES` contained null bytes"))
                .collect(),

            // These need to be initialized later as they contain pointers to the fields in this
            // descriptor
            clap_features_ptrs: Vec::new(),
            plugin_descriptor: None,

            _phantom: PhantomData,
        };

        // The keyword list is an environ-like list of char pointers terminated by a null pointer.
        descriptor.clap_features_ptrs = descriptor
            .clap_features
            .iter()
            .map(|feature| feature.as_ptr())
            .collect();
        descriptor.clap_features_ptrs.push(std::ptr::null());

        // We couldn't initialize this directly because of all the CStrings
        // NOTE: This is safe without pinning this struct because all of the data is already stored
        //       on the heap
        descriptor.plugin_descriptor = Some(clap_plugin_descriptor {
            clap_version: CLAP_VERSION,
            id: descriptor.clap_id.as_ptr(),
            name: descriptor.name.as_ptr(),
            vendor: descriptor.vendor.as_ptr(),
            url: descriptor.url.as_ptr(),
            version: descriptor.version.as_ptr(),
            manual_url: descriptor
                .clap_manual_url
                .as_ref()
                .map(|url| url.as_ptr())
                .unwrap_or(std::ptr::null()),
            support_url: descriptor
                .clap_support_url
                .as_ref()
                .map(|url| url.as_ptr())
                .unwrap_or(std::ptr::null()),
            description: descriptor
                .clap_description
                .as_ref()
                .map(|description| description.as_ptr())
                .unwrap_or(std::ptr::null()),
            features: descriptor.clap_features_ptrs.as_ptr(),
        });

        descriptor
    }
}

unsafe impl<P: ClapPlugin> Send for PluginDescriptor<P> {}
unsafe impl<P: ClapPlugin> Sync for PluginDescriptor<P> {}

impl<P: ClapPlugin> PluginDescriptor<P> {
    pub fn clap_plugin_descriptor(&self) -> &clap_plugin_descriptor {
        self.plugin_descriptor.as_ref().unwrap()
    }

    pub fn clap_id(&self) -> &CStr {
        self.clap_id.as_c_str()
    }
}