vapoursynth/vsscript/
environment.rs1use std::ffi::{CStr, CString};
2use std::fs::File;
3use std::io::Read;
4use std::ops::{Deref, DerefMut};
5use std::path::Path;
6use std::ptr;
7use std::ptr::NonNull;
8use vapoursynth_sys as ffi;
9
10use crate::api::API;
11use crate::core::CoreRef;
12use crate::map::Map;
13use crate::node::Node;
14use crate::vsscript::errors::Result;
15use crate::vsscript::*;
16
17use crate::vsscript::VSScriptError;
18
19#[derive(Debug, Clone, Copy, Eq, PartialEq)]
21pub enum EvalFlags {
22 Nothing,
23 SetWorkingDir,
25}
26
27impl EvalFlags {
28 #[inline]
29 fn ffi_type(self) -> ::std::os::raw::c_int {
30 match self {
31 EvalFlags::Nothing => 0,
32 EvalFlags::SetWorkingDir => ffi::VSEvalFlags::efSetWorkingDir as _,
33 }
34 }
35}
36
37#[derive(Clone, Copy)]
39enum EvaluateScriptArgs<'a> {
40 Script(&'a str),
42 File(&'a Path, EvalFlags),
44}
45
46#[derive(Debug)]
48pub struct Environment {
49 handle: NonNull<ffi::VSScript>,
50}
51
52unsafe impl Send for Environment {}
53unsafe impl Sync for Environment {}
54
55impl Drop for Environment {
56 #[inline]
57 fn drop(&mut self) {
58 unsafe {
59 ffi::vsscript_freeScript(self.handle.as_ptr());
60 }
61 }
62}
63
64impl Environment {
65 #[inline]
70 unsafe fn error(&self) -> CString {
71 let message = ffi::vsscript_getError(self.handle.as_ptr());
72 CStr::from_ptr(message).to_owned()
73 }
74
75 pub fn new() -> Result<Self> {
80 maybe_initialize();
81
82 let mut handle = ptr::null_mut();
83 let rv = unsafe { call_vsscript!(ffi::vsscript_createScript(&mut handle)) };
84 let environment = Self {
85 handle: unsafe { NonNull::new_unchecked(handle) },
86 };
87
88 if rv != 0 {
89 Err(VSScriptError::new(unsafe { environment.error() }).into())
90 } else {
91 Ok(environment)
92 }
93 }
94
95 fn evaluate_script(&mut self, args: EvaluateScriptArgs) -> Result<()> {
102 let (script, path, flags) = match args {
103 EvaluateScriptArgs::Script(script) => (script.to_owned(), None, EvalFlags::Nothing),
104 EvaluateScriptArgs::File(path, flags) => {
105 let mut file = File::open(path).map_err(Error::FileOpen)?;
106 let mut script = String::new();
107 file.read_to_string(&mut script).map_err(Error::FileRead)?;
108
109 let path = path.to_str().ok_or(Error::PathInvalidUnicode)?;
111 let path = CString::new(path)?;
112
113 (script, Some(path), flags)
114 }
115 };
116
117 let script = CString::new(script)?;
118
119 let rv = unsafe {
120 call_vsscript!(ffi::vsscript_evaluateScript(
121 &mut self.handle.as_ptr(),
122 script.as_ptr(),
123 path.as_ref().map(|p| p.as_ptr()).unwrap_or(ptr::null()),
124 flags.ffi_type(),
125 ))
126 };
127
128 if rv != 0 {
129 Err(VSScriptError::new(unsafe { self.error() }).into())
130 } else {
131 Ok(())
132 }
133 }
134
135 #[inline]
137 pub fn from_script(script: &str) -> Result<Self> {
138 let mut environment = Self::new()?;
139 environment.evaluate_script(EvaluateScriptArgs::Script(script))?;
140 Ok(environment)
141 }
142
143 #[inline]
145 pub fn from_file<P: AsRef<Path>>(path: P, flags: EvalFlags) -> Result<Self> {
146 let mut environment = Self::new()?;
147 environment.evaluate_script(EvaluateScriptArgs::File(path.as_ref(), flags))?;
148 Ok(environment)
149 }
150
151 #[inline]
153 pub fn eval_script(&mut self, script: &str) -> Result<()> {
154 self.evaluate_script(EvaluateScriptArgs::Script(script))
155 }
156
157 #[inline]
159 pub fn eval_file<P: AsRef<Path>>(&mut self, path: P, flags: EvalFlags) -> Result<()> {
160 self.evaluate_script(EvaluateScriptArgs::File(path.as_ref(), flags))
161 }
162
163 #[inline]
165 pub fn clear(&self) {
166 unsafe {
167 ffi::vsscript_clearEnvironment(self.handle.as_ptr());
168 }
169 }
170
171 #[cfg(all(
174 not(feature = "gte-vsscript-api-31"),
175 feature = "vapoursynth-functions"
176 ))]
177 #[inline]
178 pub fn get_output(&self, index: i32) -> Result<Node> {
179 API::get().ok_or(Error::NoAPI)?;
181
182 let node_handle = unsafe { ffi::vsscript_getOutput(self.handle.as_ptr(), index) };
183 if node_handle.is_null() {
184 Err(Error::NoOutput)
185 } else {
186 Ok(unsafe { Node::from_ptr(node_handle) })
187 }
188 }
189
190 #[cfg(all(
193 feature = "gte-vsscript-api-31",
194 any(feature = "vapoursynth-functions", feature = "gte-vsscript-api-32")
195 ))]
196 #[inline]
197 pub fn get_output(&self, index: i32) -> Result<(Node, Option<Node>)> {
198 API::get().ok_or(Error::NoAPI)?;
200
201 let mut alpha_handle = ptr::null_mut();
202 let node_handle =
203 unsafe { ffi::vsscript_getOutput2(self.handle.as_ptr(), index, &mut alpha_handle) };
204
205 if node_handle.is_null() {
206 return Err(Error::NoOutput);
207 }
208
209 let node = unsafe { Node::from_ptr(node_handle) };
210 let alpha_node = unsafe { alpha_handle.as_mut().map(|p| Node::from_ptr(p)) };
211
212 Ok((node, alpha_node))
213 }
214
215 #[inline]
217 pub fn clear_output(&self, index: i32) -> Result<()> {
218 let rv = unsafe { ffi::vsscript_clearOutput(self.handle.as_ptr(), index) };
219 if rv != 0 {
220 Err(Error::NoOutput)
221 } else {
222 Ok(())
223 }
224 }
225
226 #[cfg(any(feature = "vapoursynth-functions", feature = "gte-vsscript-api-32"))]
229 pub fn get_core(&self) -> Result<CoreRef> {
230 API::get().ok_or(Error::NoAPI)?;
232
233 let ptr = unsafe { ffi::vsscript_getCore(self.handle.as_ptr()) };
234 if ptr.is_null() {
235 Err(Error::NoCore)
236 } else {
237 Ok(unsafe { CoreRef::from_ptr(ptr) })
238 }
239 }
240
241 pub fn get_variable(&self, name: &str, map: &mut Map) -> Result<()> {
243 let name = CString::new(name)?;
244 let rv = unsafe {
245 ffi::vsscript_getVariable(self.handle.as_ptr(), name.as_ptr(), map.deref_mut())
246 };
247 if rv != 0 {
248 Err(Error::NoSuchVariable)
249 } else {
250 Ok(())
251 }
252 }
253
254 pub fn set_variables(&self, variables: &Map) -> Result<()> {
256 let rv = unsafe { ffi::vsscript_setVariable(self.handle.as_ptr(), variables.deref()) };
257 if rv != 0 {
258 Err(Error::NoSuchVariable)
259 } else {
260 Ok(())
261 }
262 }
263
264 pub fn clear_variable(&self, name: &str) -> Result<()> {
266 let name = CString::new(name)?;
267 let rv = unsafe { ffi::vsscript_clearVariable(self.handle.as_ptr(), name.as_ptr()) };
268 if rv != 0 {
269 Err(Error::NoSuchVariable)
270 } else {
271 Ok(())
272 }
273 }
274}