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::*;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum EvalFlags {
Nothing,
SetWorkingDir,
}
impl EvalFlags {
#[inline]
fn ffi_type(self) -> ::std::os::raw::c_int {
match self {
EvalFlags::Nothing => 0,
EvalFlags::SetWorkingDir => ffi::VSEvalFlags::efSetWorkingDir as _,
}
}
}
#[derive(Clone, Copy)]
enum EvaluateScriptArgs<'a> {
Script(&'a str),
File(&'a Path, EvalFlags),
}
#[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 {
#[inline]
unsafe fn error(&self) -> CString {
let message = ffi::vsscript_getError(self.handle.as_ptr());
CStr::from_ptr(message).to_owned()
}
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)
}
}
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)?;
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(())
}
}
#[inline]
pub fn from_script(script: &str) -> Result<Self> {
let mut environment = Self::new()?;
environment.evaluate_script(EvaluateScriptArgs::Script(script))?;
Ok(environment)
}
#[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)
}
#[inline]
pub fn eval_script(&mut self, script: &str) -> Result<()> {
self.evaluate_script(EvaluateScriptArgs::Script(script))
}
#[inline]
pub fn eval_file<P: AsRef<Path>>(&mut self, path: P, flags: EvalFlags) -> Result<()> {
self.evaluate_script(EvaluateScriptArgs::File(path.as_ref(), flags))
}
#[inline]
pub fn clear(&self) {
unsafe {
ffi::vsscript_clearEnvironment(self.handle.as_ptr());
}
}
#[cfg(all(not(feature = "gte-vsscript-api-31"), feature = "vapoursynth-functions"))]
#[inline]
pub fn get_output(&self, index: i32) -> Result<Node> {
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) })
}
}
#[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>)> {
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))
}
#[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(())
}
}
#[cfg(any(feature = "vapoursynth-functions", feature = "gte-vsscript-api-32"))]
pub fn get_core(&self) -> Result<CoreRef> {
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) })
}
}
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(())
}
}
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(())
}
}
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(())
}
}
}