clap_clap/ext/
state.rs

1use std::fmt::{Display, Formatter};
2
3use crate::{
4    plugin::Plugin,
5    stream::{IStream, OStream},
6};
7
8pub trait State<P: Plugin> {
9    /// Saves the plugin state into stream.
10    ///
11    /// # Return
12    ///
13    /// Returns `Ok` if the state was correctly saved.
14    fn save(plugin: &P, stream: &mut OStream) -> Result<(), crate::Error>;
15
16    /// Loads the plugin state from stream.
17    ///
18    /// # Return
19    ///
20    /// Returns `Ok` if the state was correctly restored.
21    fn load(plugin: &P, stream: &mut IStream) -> Result<(), crate::Error>;
22}
23
24pub(crate) use ffi::PluginState;
25
26use crate::{ffi::clap_host_state, host::Host};
27
28mod ffi {
29    use std::marker::PhantomData;
30
31    use crate::{
32        ext::state::State,
33        ffi::{clap_istream, clap_ostream, clap_plugin, clap_plugin_state},
34        plugin::{ClapPlugin, Plugin},
35        stream::{IStream, OStream},
36    };
37
38    extern "C-unwind" fn save<E, P>(plugin: *const clap_plugin, stream: *const clap_ostream) -> bool
39    where
40        E: State<P>,
41        P: Plugin,
42    {
43        if plugin.is_null() {
44            return false;
45        }
46        // SAFETY: We just checked that the pointer is non-null and the plugin
47        // has been obtained from host and is tied to type P.
48        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
49
50        // SAFETY: This function is called on the main thread.
51        // It is guaranteed that we are the only function accessing the plugin now.
52        // So the mutable reference to plugin for the duration of this call is
53        // safe.
54        let plugin = unsafe { clap_plugin.plugin() };
55
56        if unsafe { stream.as_ref().and_then(|s| s.write) }.is_none() {
57            return false;
58        }
59        // SAFETY: We just checked if both stream and stream.write are non-null.
60        let mut stream = unsafe { OStream::new_unchecked(stream) };
61
62        E::save(plugin, &mut stream).is_ok()
63    }
64
65    extern "C-unwind" fn load<E, P>(plugin: *const clap_plugin, stream: *const clap_istream) -> bool
66    where
67        E: State<P>,
68        P: Plugin,
69    {
70        if plugin.is_null() {
71            return false;
72        }
73        // SAFETY: We just checked that the pointer is non-null and the plugin
74        // has been obtained from host and is tied to type P.
75        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
76
77        // SAFETY: This function is called on the main thread.
78        // It is guaranteed that we are the only function accessing the plugin now.
79        // So the mutable reference to plugin for the duration of this call is
80        // safe.
81        let plugin = unsafe { clap_plugin.plugin() };
82
83        if unsafe { stream.as_ref().and_then(|s| s.read) }.is_none() {
84            return false;
85        }
86        // SAFETY: We just checked if both stream and stream.read are non-null.
87        let mut stream = unsafe { IStream::new_unchecked(stream) };
88
89        E::load(plugin, &mut stream).is_ok()
90    }
91
92    pub(crate) struct PluginState<P> {
93        #[allow(unused)]
94        clap_plugin_state: clap_plugin_state,
95        _marker: PhantomData<P>,
96    }
97
98    impl<P: Plugin> PluginState<P> {
99        pub(crate) fn new<E: State<P>>(_: E) -> Self {
100            Self {
101                clap_plugin_state: clap_plugin_state {
102                    save: Some(save::<E, P>),
103                    load: Some(load::<E, P>),
104                },
105                _marker: PhantomData,
106            }
107        }
108    }
109}
110
111impl<P: Plugin> State<P> for () {
112    fn save(_: &P, _: &mut OStream) -> Result<(), crate::Error> {
113        Ok(())
114    }
115
116    fn load(_: &P, _: &mut IStream) -> Result<(), crate::Error> {
117        Ok(())
118    }
119}
120
121#[derive(Debug)]
122pub struct HostState<'a> {
123    host: &'a Host,
124    clap_host_state: &'a clap_host_state,
125}
126
127impl<'a> HostState<'a> {
128    /// # Safety
129    ///
130    /// All extension interface function pointers must be non-null (Some), and
131    /// the functions must be thread-safe.
132    pub(crate) const unsafe fn new_unchecked(
133        host: &'a Host,
134        clap_host_state: &'a clap_host_state,
135    ) -> Self {
136        Self {
137            host,
138            clap_host_state,
139        }
140    }
141
142    pub fn make_dirty(&self) {
143        // SAFETY: By construction, the callback must be a valid function pointer,
144        // and the call is thread-safe.
145        let callback = self.clap_host_state.mark_dirty.unwrap();
146        unsafe { callback(self.host.clap_host()) }
147    }
148}
149
150#[derive(Debug)]
151pub enum Error {
152    Read,
153    Write,
154    Eof,
155}
156
157impl Display for Error {
158    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
159        use Error::*;
160        match self {
161            Read => write!(f, "read error"),
162            Write => write!(f, "write error"),
163            Eof => write!(f, "end of file"),
164        }
165    }
166}
167
168impl std::error::Error for Error {}
169
170impl From<Error> for crate::Error {
171    fn from(value: Error) -> Self {
172        crate::ext::Error::State(value).into()
173    }
174}