vapoursynth4_rs/
sciprt.rs1use std::{
2 ffi::{CStr, c_int},
3 ptr::{NonNull, null_mut},
4};
5
6use thiserror::Error;
7use vapoursynth4_sys::VAPOURSYNTH_API_VERSION;
8
9use crate::{
10 api::{Api, VssApi},
11 core::{Core, CoreRef},
12 node::{AudioNode, VideoNode},
13};
14
15use super::ffi;
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub struct Script {
19 handle: NonNull<ffi::VSScript>,
20 vssapi: VssApi,
21 api: Api,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25pub enum OutputNode {
26 Audio(AudioNode),
27 Video(VideoNode),
28}
29
30impl OutputNode {
31 #[must_use]
32 pub fn as_audio(&self) -> Option<&AudioNode> {
33 if let OutputNode::Audio(node) = self {
34 Some(node)
35 } else {
36 None
37 }
38 }
39
40 #[must_use]
41 pub fn as_video(&self) -> Option<&VideoNode> {
42 if let OutputNode::Video(node) = self {
43 Some(node)
44 } else {
45 None
46 }
47 }
48}
49
50impl Script {
51 pub fn new(core: Option<&Core>, vssapi: VssApi) -> Self {
57 unsafe {
58 let handle = NonNull::new((vssapi.createScript)(core.map_or(null_mut(), Core::as_ptr)))
59 .expect("Failed to create script");
60 let api_ptr = (vssapi.getVSAPI)(VAPOURSYNTH_API_VERSION);
61 assert!(!api_ptr.is_null());
62 let api = Api::from_ptr(api_ptr);
63 Self {
64 handle,
65 vssapi,
66 api,
67 }
68 }
69 }
70
71 #[must_use]
72 pub fn get_api(&self) -> Api {
73 self.api
74 }
75
76 pub fn core(&self) -> Result<CoreRef<'_>, ScriptError> {
82 unsafe {
83 let core = (self.vssapi.getCore)(self.handle.as_ptr());
84 self.get_ptr_error(core)
85 .map(|core| CoreRef::from_ptr(core, self.api))
86 }
87 }
88
89 pub fn evaluate(&self, buffer: &CStr, filename: &CStr) -> Result<(), ScriptError> {
95 unsafe {
96 let result = (self.vssapi.evaluateBuffer)(
97 self.handle.as_ptr(),
98 buffer.as_ptr(),
99 filename.as_ptr(),
100 );
101 self.get_error(result)
102 }
103 }
104
105 pub fn evaluate_file(&self, filename: &CStr) -> Result<(), ScriptError> {
111 unsafe {
112 let result = (self.vssapi.evaluateFile)(self.handle.as_ptr(), filename.as_ptr());
113 self.get_error(result)
114 }
115 }
116
117 pub fn get_output(&self, index: c_int) -> Result<OutputNode, ScriptError> {
123 unsafe {
124 let ptr =
125 self.get_ptr_error((self.vssapi.getOutputNode)(self.handle.as_ptr(), index))?;
126 match (self.api.getNodeType)(ptr) {
127 ffi::VSMediaType::Audio => {
128 Ok(OutputNode::Audio(AudioNode::from_ptr(ptr, self.api)))
129 }
130 ffi::VSMediaType::Video => {
131 Ok(OutputNode::Video(VideoNode::from_ptr(ptr, self.api)))
132 }
133 }
134 }
135 }
136}
137
138impl Script {
140 fn get_error(&self, ret: c_int) -> Result<(), ScriptError> {
141 if ret == 0 {
142 Ok(())
143 } else {
144 Err(unsafe { ScriptError::from_vss(self) })
145 }
146 }
147
148 fn get_ptr_error<T>(&self, ptr: *mut T) -> Result<*mut T, ScriptError> {
149 if ptr.is_null() {
150 Err(unsafe { ScriptError::from_vss(self) })
151 } else {
152 Ok(ptr)
153 }
154 }
155}
156
157impl Drop for Script {
158 fn drop(&mut self) {
159 unsafe { (self.vssapi.freeScript)(self.handle.as_ptr()) };
160 }
161}
162
163#[cfg(feature = "link-vsscript")]
164impl Default for Script {
165 fn default() -> Self {
166 Self::new(None, VssApi::default())
167 }
168}
169
170#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
173#[error("VSScript error: {0}")]
174pub struct ScriptError(String);
175
176impl ScriptError {
177 unsafe fn from_vss(vss: &Script) -> Self {
178 unsafe {
179 Self(
180 CStr::from_ptr((vss.vssapi.getError)(vss.handle.as_ptr()))
181 .to_string_lossy()
182 .into_owned(),
183 )
184 }
185 }
186}