rustsynth/
plugin.rs

1use bitflags::bitflags;
2use ffi::VSPluginFunction;
3use rustsynth_sys::{self as ffi, VSPluginConfigFlags};
4use std::{
5    ffi::{CStr, CString},
6    marker::PhantomData,
7    ops::Deref,
8    ptr::{self, NonNull},
9};
10
11use crate::{api::API, map::OwnedMap, prelude::Map};
12
13/// A VapourSynth plugin.
14///
15/// There are a few of these built into the core, and therefore available at all times: the basic filters (identifier `com.vapoursynth.std`, namespace `std`), the resizers (identifier `com.vapoursynth.resize`, namespace `resize`), and the Avisynth compatibility module, if running in Windows (identifier `com.vapoursynth.avisynth`, namespace `avs`).
16#[derive(Debug, Clone, Copy)]
17pub struct Plugin<'core> {
18    handle: NonNull<ffi::VSPlugin>,
19    _owner: PhantomData<&'core ()>,
20}
21
22unsafe impl<'core> Send for Plugin<'core> {}
23unsafe impl<'core> Sync for Plugin<'core> {}
24
25impl<'core> Plugin<'core> {
26    #[inline]
27    pub unsafe fn from_ptr(ptr: *mut ffi::VSPlugin) -> Self {
28        Plugin {
29            handle: NonNull::new_unchecked(ptr),
30            _owner: PhantomData,
31        }
32    }
33
34    /// Returns the underlying pointer.
35    #[inline]
36    pub(crate) fn ptr(&self) -> *mut ffi::VSPlugin {
37        self.handle.as_ptr()
38    }
39
40    /// The path to the shared object of the plugin or `None` if is a internal VapourSynth plugin
41    pub fn path(&self) -> Option<String> {
42        let ptr = unsafe { API::get_cached().get_plugin_path(self.ptr()) };
43        if ptr.is_null() {
44            None
45        } else {
46            Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() })
47        }
48    }
49
50    /// The id associated with the plugin or `None` if it has no id set
51    pub fn id(&self) -> Option<String> {
52        let ptr = unsafe { API::get_cached().get_plugin_id(self.ptr()) };
53        if ptr.is_null() {
54            None
55        } else {
56            Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() })
57        }
58    }
59
60    /// The namespace associated with the plugin or `None` if it has no namespace set
61    pub fn namespace(&self) -> Option<String> {
62        let ptr = unsafe { API::get_cached().get_plugin_ns(self.ptr()) };
63        if ptr.is_null() {
64            None
65        } else {
66            Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() })
67        }
68    }
69
70    /// The name associated with the plugin or `None` if it has no name set
71    pub fn name(&self) -> Option<String> {
72        let ptr = unsafe { API::get_cached().get_plugin_name(self.ptr()) };
73        if ptr.is_null() {
74            None
75        } else {
76            Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() })
77        }
78    }
79
80    #[inline]
81    pub fn version(&self) -> i32 {
82        unsafe { API::get_cached().get_plugin_version(self.ptr()) }
83    }
84
85    /// Get function struct associated with the name
86    ///
87    /// returns `None` if no function is found
88    pub fn function(&self, name: &str) -> Option<PluginFunction<'core>> {
89        let name_ptr = CString::new(name).unwrap();
90        unsafe {
91            let ptr = API::get_cached().get_plugin_function_by_name(name_ptr.as_ptr(), self.ptr());
92            if ptr.is_null() {
93                None
94            } else {
95                Some(PluginFunction::from_ptr(ptr))
96            }
97        }
98    }
99
100    /// Creates an iterator over all the functions of the plugin in an arbitrary order
101    pub fn functions(&'_ self) -> PluginFunctions<'_> {
102        PluginFunctions {
103            function: None,
104            plugin: self,
105        }
106    }
107
108    fn next_function(&self, function: Option<PluginFunction>) -> Option<PluginFunction<'core>> {
109        unsafe {
110            let function = if let Some(value) = function {
111                value.ptr()
112            } else {
113                ptr::null_mut()
114            };
115            let ptr = API::get_cached().get_next_plugin_function(function, self.ptr());
116            if ptr.is_null() {
117                None
118            } else {
119                Some(PluginFunction::from_ptr(ptr))
120            }
121        }
122    }
123
124    /// Invokes the plugin function with the name provided
125    ///
126    /// # Panics
127    ///
128    /// Will panic if there is no function with that name
129    pub fn invoke(&self, name: &str, args: &Map<'core>) -> OwnedMap<'core> {
130        self.function(name).expect("No Plugin found");
131        let name = CString::new(name).unwrap();
132        unsafe {
133            OwnedMap::from_ptr(API::get_cached().invoke(
134                self.handle.as_ptr(),
135                name.as_ptr(),
136                args.deref(),
137            ))
138        }
139    }
140}
141
142bitflags! {
143    pub struct PluginConfigFlags: i32 {
144        /// Allow functions to be added to the plugin object after the plugin loading phase. Mostly useful for Avisynth compatibility and other foreign plugin loaders.
145        const MODIFIABLE = 1;
146        const NONE  = 0;
147    }
148}
149
150impl PluginConfigFlags {
151    pub fn as_ptr(&self) -> ffi::VSPluginConfigFlags {
152        VSPluginConfigFlags(self.bits.try_into().unwrap())
153    }
154}
155
156/// The iterator over the functions found in a plugin
157///
158/// created by [Plugin::functions()]
159#[derive(Debug, Clone, Copy)]
160pub struct PluginFunctions<'core> {
161    function: Option<PluginFunction<'core>>,
162    plugin: &'core Plugin<'core>,
163}
164
165impl<'core> Iterator for PluginFunctions<'core> {
166    type Item = PluginFunction<'core>;
167
168    fn next(&mut self) -> Option<Self::Item> {
169        self.function = self.plugin.next_function(self.function);
170        self.function
171    }
172}
173
174/// A function of a plugin
175#[derive(Debug, Clone, Copy)]
176pub struct PluginFunction<'core> {
177    ptr: NonNull<ffi::VSPluginFunction>,
178    pub name: Option<&'core str>,
179    pub arguments: Option<&'core str>,
180}
181
182impl<'core> PluginFunction<'core> {
183    pub(crate) unsafe fn from_ptr(ptr: *mut VSPluginFunction) -> Self {
184        let name_ptr = unsafe { API::get_cached().get_plugin_function_name(ptr) };
185        let name = if name_ptr.is_null() {
186            None
187        } else {
188            Some(unsafe { CStr::from_ptr(name_ptr).to_str().unwrap() })
189        };
190
191        let arg_ptr = unsafe { API::get_cached().get_plugin_function_arguments(ptr) };
192        let arguments = if arg_ptr.is_null() {
193            None
194        } else {
195            Some(unsafe { CStr::from_ptr(arg_ptr).to_str().unwrap() })
196        };
197        PluginFunction {
198            ptr: NonNull::new_unchecked(ptr),
199            name,
200            arguments,
201        }
202    }
203
204    fn ptr(&self) -> *mut ffi::VSPluginFunction {
205        self.ptr.as_ptr()
206    }
207}