use std::ffi::{CStr, CString};
use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::ptr;
use std::ptr::NonNull;
use vapoursynth_sys as ffi;
use crate::api::API;
use crate::core::CoreRef;
use crate::map::Map;
use crate::node::Node;
use crate::vsscript::errors::Result;
use crate::vsscript::*;
use crate::vsscript::VSScriptError;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum EvalFlags {
Nothing,
SetWorkingDir,
}
#[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) {
let api = VSScriptAPI::get().expect("VSScript API not available");
unsafe {
(api.handle().freeScript.unwrap())(self.handle.as_ptr());
}
}
}
impl Environment {
#[inline]
unsafe fn error(&self) -> CString {
let api = VSScriptAPI::get().expect("VSScript API not available");
let message = (api.handle().getError.unwrap())(self.handle.as_ptr());
CStr::from_ptr(message).to_owned()
}
pub fn new() -> Result<Self> {
maybe_initialize();
let api = VSScriptAPI::get().expect("VSScript API not available");
let handle = unsafe { (api.handle().createScript.unwrap())(ptr::null_mut()) };
if handle.is_null() {
Err(Error::ScriptCreationFailed)
} else {
Ok(Self {
handle: unsafe { NonNull::new_unchecked(handle) },
})
}
}
fn evaluate_script(&mut self, args: EvaluateScriptArgs) -> Result<()> {
let api = VSScriptAPI::get().expect("VSScript API not available");
let rv = match args {
EvaluateScriptArgs::Script(script) => {
let script = CString::new(script)?;
let filename = CString::new("<string>").unwrap();
unsafe {
(api.handle().evaluateBuffer.unwrap())(
self.handle.as_ptr(),
script.as_ptr(),
filename.as_ptr(),
)
}
}
EvaluateScriptArgs::File(path, flags) => {
if flags == EvalFlags::SetWorkingDir {
unsafe {
(api.handle().evalSetWorkingDir.unwrap())(self.handle.as_ptr(), 1);
}
}
let path_str = path.to_str().ok_or(Error::PathInvalidUnicode)?;
let path_cstr = CString::new(path_str)?;
let rv = unsafe {
(api.handle().evaluateFile.unwrap())(self.handle.as_ptr(), path_cstr.as_ptr())
};
if flags == EvalFlags::SetWorkingDir {
unsafe {
(api.handle().evalSetWorkingDir.unwrap())(self.handle.as_ptr(), 0);
}
}
rv
}
};
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) {
}
#[inline]
pub fn get_output(&self, index: i32) -> Result<(Node<'_>, Option<Node<'_>>)> {
API::get().ok_or(Error::NoAPI)?;
let vsscript_api = VSScriptAPI::get().expect("VSScript API not available");
let node_handle =
unsafe { (vsscript_api.handle().getOutputNode.unwrap())(self.handle.as_ptr(), index) };
if node_handle.is_null() {
return Err(Error::NoOutput);
}
let node = unsafe { Node::from_ptr(node_handle) };
let alpha_handle = unsafe {
(vsscript_api.handle().getOutputAlphaNode.unwrap())(self.handle.as_ptr(), index)
};
let alpha_node = if alpha_handle.is_null() {
None
} else {
Some(unsafe { Node::from_ptr(alpha_handle) })
};
Ok((node, alpha_node))
}
#[inline]
pub fn clear_output(&self, _index: i32) -> Result<()> {
Ok(())
}
pub fn get_core(&self) -> Result<CoreRef<'_>> {
API::get().ok_or(Error::NoAPI)?;
let vsscript_api = VSScriptAPI::get().expect("VSScript API not available");
let ptr = unsafe { (vsscript_api.handle().getCore.unwrap())(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 vsscript_api = VSScriptAPI::get().expect("VSScript API not available");
let name = CString::new(name)?;
let rv = unsafe {
(vsscript_api.handle().getVariable.unwrap())(
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 vsscript_api = VSScriptAPI::get().expect("VSScript API not available");
let rv = unsafe {
(vsscript_api.handle().setVariables.unwrap())(self.handle.as_ptr(), variables.deref())
};
if rv != 0 {
Err(Error::NoSuchVariable)
} else {
Ok(())
}
}
#[inline]
pub fn clear_variable(&self, _name: &str) -> Result<()> {
Ok(())
}
}