vapoursynth4_rs/
sciprt.rs

1use std::{
2    ffi::{CStr, c_int},
3    ptr::{NonNull, null_mut},
4};
5
6use thiserror::Error;
7
8use crate::{
9    api::{Api, VssApi},
10    core::{Core, CoreRef},
11    node::{AudioNode, VideoNode},
12};
13
14use super::ffi;
15
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct Script {
18    handle: NonNull<ffi::VSScript>,
19    vssapi: VssApi,
20    api: Api,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub enum OutputNode {
25    Audio(AudioNode),
26    Video(VideoNode),
27}
28
29impl Script {
30    /// Creates a new script instance.
31    ///
32    /// # Panics
33    ///
34    /// Panics if the script creation fails.
35    pub fn new(core: Option<&Core>, vssapi: VssApi, api: Api) -> Self {
36        unsafe {
37            let handle = NonNull::new((vssapi.createScript)(core.map_or(null_mut(), Core::as_ptr)))
38                .expect("Failed to create script");
39            Self {
40                handle,
41                vssapi,
42                api,
43            }
44        }
45    }
46
47    #[must_use]
48    pub fn get_api(&self) -> Api {
49        self.api
50    }
51
52    /// Returns a reference to the core associated with this script.
53    ///
54    /// # Errors
55    ///
56    /// Returns an error message if the core could not be retrieved.
57    pub fn core(&self) -> Result<CoreRef, ScriptError> {
58        unsafe {
59            let core = (self.vssapi.getCore)(self.handle.as_ptr());
60            self.get_ptr_error(core)
61                .map(|core| CoreRef::from_ptr(core, self.api))
62        }
63    }
64
65    /// Evaluates a script buffer with the given filename.
66    ///
67    /// # Errors
68    ///
69    /// Returns an error message if the script evaluation fails.
70    pub fn evaluate(&self, buffer: &CStr, filename: &CStr) -> Result<(), ScriptError> {
71        unsafe {
72            let result = (self.vssapi.evaluateBuffer)(
73                self.handle.as_ptr(),
74                buffer.as_ptr(),
75                filename.as_ptr(),
76            );
77            self.get_error(result)
78        }
79    }
80
81    /// Evaluates a script from a file.
82    ///
83    /// # Errors
84    ///
85    /// Returns an error message if the script evaluation fails.
86    pub fn evaluate_file(&self, filename: &CStr) -> Result<(), ScriptError> {
87        unsafe {
88            let result = (self.vssapi.evaluateFile)(self.handle.as_ptr(), filename.as_ptr());
89            self.get_error(result)
90        }
91    }
92
93    /// Gets the output node at the specified index.
94    ///
95    /// # Errors
96    ///
97    /// Returns a `ScriptError` if the output node could not be retrieved.
98    pub fn get_output(&self, index: c_int) -> Result<OutputNode, ScriptError> {
99        unsafe {
100            let ptr =
101                self.get_ptr_error((self.vssapi.getOutputNode)(self.handle.as_ptr(), index))?;
102            match (self.api.getNodeType)(ptr) {
103                ffi::VSMediaType::Audio => {
104                    Ok(OutputNode::Audio(AudioNode::from_ptr(ptr, self.api)))
105                }
106                ffi::VSMediaType::Video => {
107                    Ok(OutputNode::Video(VideoNode::from_ptr(ptr, self.api)))
108                }
109            }
110        }
111    }
112}
113
114// MARK: Helper
115impl Script {
116    fn get_error(&self, ret: c_int) -> Result<(), ScriptError> {
117        if ret == 0 {
118            Ok(())
119        } else {
120            Err(unsafe { ScriptError::from_vss(self) })
121        }
122    }
123
124    fn get_ptr_error<T>(&self, ptr: *mut T) -> Result<*mut T, ScriptError> {
125        if ptr.is_null() {
126            Err(unsafe { ScriptError::from_vss(self) })
127        } else {
128            Ok(ptr)
129        }
130    }
131}
132
133impl Drop for Script {
134    fn drop(&mut self) {
135        unsafe { (self.vssapi.freeScript)(self.handle.as_ptr()) };
136    }
137}
138
139#[cfg(feature = "link-library")]
140impl Default for Script {
141    fn default() -> Self {
142        Self::new(None, VssApi::default(), Api::default())
143    }
144}
145
146// MARK: ScriptError
147
148#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
149#[error("VSScript error: {0}")]
150pub struct ScriptError(String);
151
152impl ScriptError {
153    unsafe fn from_vss(vss: &Script) -> Self {
154        unsafe {
155            Self(
156                CStr::from_ptr((vss.vssapi.getError)(vss.handle.as_ptr()))
157                    .to_string_lossy()
158                    .into_owned(),
159            )
160        }
161    }
162}