279 lines
8.8 KiB
Rust
279 lines
8.8 KiB
Rust
use std::ffi::{CStr, CString};
|
|
use std::fs::File;
|
|
use std::io::Read;
|
|
use std::ops::{Deref, DerefMut};
|
|
use std::path::Path;
|
|
use std::ptr;
|
|
use std::ptr::NonNull;
|
|
use vapoursynth_sys as ffi;
|
|
|
|
use api::API;
|
|
use core::CoreRef;
|
|
use map::Map;
|
|
use node::Node;
|
|
use vsscript::errors::Result;
|
|
use vsscript::*;
|
|
|
|
/// VSScript file evaluation flags.
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
pub enum EvalFlags {
|
|
Nothing,
|
|
/// The working directory will be changed to the script's directory for the evaluation.
|
|
SetWorkingDir,
|
|
}
|
|
|
|
impl EvalFlags {
|
|
#[inline]
|
|
fn ffi_type(self) -> ::std::os::raw::c_int {
|
|
match self {
|
|
EvalFlags::Nothing => 0,
|
|
EvalFlags::SetWorkingDir => ffi::VSEvalFlags::efSetWorkingDir as _,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Contains two possible variants of arguments to `Environment::evaluate_script()`.
|
|
#[derive(Clone, Copy)]
|
|
enum EvaluateScriptArgs<'a> {
|
|
/// Evaluate a script contained in the string.
|
|
Script(&'a str),
|
|
/// Evaluate a script contained in the file.
|
|
File(&'a Path, EvalFlags),
|
|
}
|
|
|
|
/// A wrapper for the VSScript environment.
|
|
#[derive(Debug)]
|
|
pub struct Environment {
|
|
handle: NonNull<ffi::VSScript>,
|
|
}
|
|
|
|
unsafe impl Send for Environment {}
|
|
unsafe impl Sync for Environment {}
|
|
|
|
impl Drop for Environment {
|
|
#[inline]
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
ffi::vsscript_freeScript(self.handle.as_ptr());
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Environment {
|
|
/// Retrieves the VSScript error message.
|
|
///
|
|
/// # Safety
|
|
/// This function must only be called if an error is present.
|
|
#[inline]
|
|
unsafe fn error(&self) -> CString {
|
|
let message = ffi::vsscript_getError(self.handle.as_ptr());
|
|
CStr::from_ptr(message).to_owned()
|
|
}
|
|
|
|
/// Creates an empty script environment.
|
|
///
|
|
/// Useful if it is necessary to set some variable in the script environment before evaluating
|
|
/// any scripts.
|
|
pub fn new() -> Result<Self> {
|
|
maybe_initialize();
|
|
|
|
let mut handle = ptr::null_mut();
|
|
let rv = unsafe { call_vsscript!(ffi::vsscript_createScript(&mut handle)) };
|
|
let environment = Self {
|
|
handle: unsafe { NonNull::new_unchecked(handle) },
|
|
};
|
|
|
|
if rv != 0 {
|
|
Err(VSScriptError::new(unsafe { environment.error() }).into())
|
|
} else {
|
|
Ok(environment)
|
|
}
|
|
}
|
|
|
|
/// Calls `vsscript_evaluateScript()`.
|
|
///
|
|
/// `self` is taken by a mutable reference mainly to ensure the atomicity of a call to
|
|
/// `vsscript_evaluateScript()` (a function that could produce an error) and the following call
|
|
/// to `vsscript_getError()`. If atomicity is not enforced, another thread could perform some
|
|
/// operation between these two and clear or change the error message.
|
|
fn evaluate_script(&mut self, args: EvaluateScriptArgs) -> Result<()> {
|
|
let (script, path, flags) = match args {
|
|
EvaluateScriptArgs::Script(script) => (script.to_owned(), None, EvalFlags::Nothing),
|
|
EvaluateScriptArgs::File(path, flags) => {
|
|
let mut file = File::open(path).map_err(Error::FileOpen)?;
|
|
let mut script = String::new();
|
|
file.read_to_string(&mut script).map_err(Error::FileRead)?;
|
|
|
|
// vsscript throws an error if it's not valid UTF-8 anyway.
|
|
let path = path.to_str().ok_or(Error::PathInvalidUnicode)?;
|
|
let path = CString::new(path)?;
|
|
|
|
(script, Some(path), flags)
|
|
}
|
|
};
|
|
|
|
let script = CString::new(script)?;
|
|
|
|
let rv = unsafe {
|
|
call_vsscript!(ffi::vsscript_evaluateScript(
|
|
&mut self.handle.as_ptr(),
|
|
script.as_ptr(),
|
|
path.as_ref().map(|p| p.as_ptr()).unwrap_or(ptr::null()),
|
|
flags.ffi_type(),
|
|
))
|
|
};
|
|
|
|
if rv != 0 {
|
|
Err(VSScriptError::new(unsafe { self.error() }).into())
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Creates a script environment and evaluates a script contained in a string.
|
|
#[inline]
|
|
pub fn from_script(script: &str) -> Result<Self> {
|
|
let mut environment = Self::new()?;
|
|
environment.evaluate_script(EvaluateScriptArgs::Script(script))?;
|
|
Ok(environment)
|
|
}
|
|
|
|
/// Creates a script environment and evaluates a script contained in a file.
|
|
#[inline]
|
|
pub fn from_file<P: AsRef<Path>>(path: P, flags: EvalFlags) -> Result<Self> {
|
|
let mut environment = Self::new()?;
|
|
environment.evaluate_script(EvaluateScriptArgs::File(path.as_ref(), flags))?;
|
|
Ok(environment)
|
|
}
|
|
|
|
/// Evaluates a script contained in a string.
|
|
#[inline]
|
|
pub fn eval_script(&mut self, script: &str) -> Result<()> {
|
|
self.evaluate_script(EvaluateScriptArgs::Script(script))
|
|
}
|
|
|
|
/// Evaluates a script contained in a file.
|
|
#[inline]
|
|
pub fn eval_file<P: AsRef<Path>>(&mut self, path: P, flags: EvalFlags) -> Result<()> {
|
|
self.evaluate_script(EvaluateScriptArgs::File(path.as_ref(), flags))
|
|
}
|
|
|
|
/// Clears the script environment.
|
|
#[inline]
|
|
pub fn clear(&self) {
|
|
unsafe {
|
|
ffi::vsscript_clearEnvironment(self.handle.as_ptr());
|
|
}
|
|
}
|
|
|
|
/// Retrieves a node from the script environment. A node in the script must have been marked
|
|
/// for output with the requested index.
|
|
#[cfg(all(
|
|
not(feature = "gte-vsscript-api-31"),
|
|
feature = "vapoursynth-functions"
|
|
))]
|
|
#[inline]
|
|
pub fn get_output(&self, index: i32) -> Result<Node> {
|
|
// Node needs the API.
|
|
API::get().ok_or(Error::NoAPI)?;
|
|
|
|
let node_handle = unsafe { ffi::vsscript_getOutput(self.handle.as_ptr(), index) };
|
|
if node_handle.is_null() {
|
|
Err(Error::NoOutput)
|
|
} else {
|
|
Ok(unsafe { Node::from_ptr(node_handle) })
|
|
}
|
|
}
|
|
|
|
/// Retrieves a node from the script environment. A node in the script must have been marked
|
|
/// for output with the requested index. The second node, if any, contains the alpha clip.
|
|
#[cfg(all(
|
|
feature = "gte-vsscript-api-31",
|
|
any(
|
|
feature = "vapoursynth-functions",
|
|
feature = "gte-vsscript-api-32"
|
|
)
|
|
))]
|
|
#[inline]
|
|
pub fn get_output(&self, index: i32) -> Result<(Node, Option<Node>)> {
|
|
// Node needs the API.
|
|
API::get().ok_or(Error::NoAPI)?;
|
|
|
|
let mut alpha_handle = ptr::null_mut();
|
|
let node_handle =
|
|
unsafe { ffi::vsscript_getOutput2(self.handle.as_ptr(), index, &mut alpha_handle) };
|
|
|
|
if node_handle.is_null() {
|
|
return Err(Error::NoOutput);
|
|
}
|
|
|
|
let node = unsafe { Node::from_ptr(node_handle) };
|
|
let alpha_node = unsafe { alpha_handle.as_mut().map(|p| Node::from_ptr(p)) };
|
|
|
|
Ok((node, alpha_node))
|
|
}
|
|
|
|
/// Cancels a node set for output. The node will no longer be available to `get_output()`.
|
|
#[inline]
|
|
pub fn clear_output(&self, index: i32) -> Result<()> {
|
|
let rv = unsafe { ffi::vsscript_clearOutput(self.handle.as_ptr(), index) };
|
|
if rv != 0 {
|
|
Err(Error::NoOutput)
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Retrieves the VapourSynth core that was created in the script environment. If a VapourSynth
|
|
/// core has not been created yet, it will be created now, with the default options.
|
|
#[cfg(any(
|
|
feature = "vapoursynth-functions",
|
|
feature = "gte-vsscript-api-32"
|
|
))]
|
|
pub fn get_core(&self) -> Result<CoreRef> {
|
|
// CoreRef needs the API.
|
|
API::get().ok_or(Error::NoAPI)?;
|
|
|
|
let ptr = unsafe { ffi::vsscript_getCore(self.handle.as_ptr()) };
|
|
if ptr.is_null() {
|
|
Err(Error::NoCore)
|
|
} else {
|
|
Ok(unsafe { CoreRef::from_ptr(ptr) })
|
|
}
|
|
}
|
|
|
|
/// Retrieves a variable from the script environment.
|
|
pub fn get_variable(&self, name: &str, map: &mut Map) -> Result<()> {
|
|
let name = CString::new(name)?;
|
|
let rv = unsafe {
|
|
ffi::vsscript_getVariable(self.handle.as_ptr(), name.as_ptr(), map.deref_mut())
|
|
};
|
|
if rv != 0 {
|
|
Err(Error::NoSuchVariable)
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Sets variables in the script environment.
|
|
pub fn set_variables(&self, variables: &Map) -> Result<()> {
|
|
let rv = unsafe { ffi::vsscript_setVariable(self.handle.as_ptr(), variables.deref()) };
|
|
if rv != 0 {
|
|
Err(Error::NoSuchVariable)
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Deletes a variable from the script environment.
|
|
pub fn clear_variable(&self, name: &str) -> Result<()> {
|
|
let name = CString::new(name)?;
|
|
let rv = unsafe { ffi::vsscript_clearVariable(self.handle.as_ptr(), name.as_ptr()) };
|
|
if rv != 0 {
|
|
Err(Error::NoSuchVariable)
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|