use std::num::{
NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
};
use std::ptr::NonNull;
#[derive(Debug)]
pub struct ScriptModuleCallHandle {
pub(crate) internal: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
pub(crate) read_section: crate::generic::ReadSection,
}
#[derive(thiserror::Error, Debug)]
pub enum ScriptModuleCallHandleError<E: std::fmt::Debug = std::convert::Infallible> {
#[error("expected {expected:?}, but got {actual:?}")]
TypeMismatch {
expected: ParamType,
actual: ParamType,
},
#[error("key contains null byte")]
KeyContainsNullByte(std::ffi::NulError),
#[error("value contains null byte")]
ValueContainsNullByte(std::ffi::NulError),
#[error("too many elements")]
TooManyElements,
#[error("failed to convert value: {0}")]
ConversionError(E),
}
pub type ScriptModuleCallHandleResult<T, E = std::convert::Infallible> =
std::result::Result<T, ScriptModuleCallHandleError<E>>;
impl ScriptModuleCallHandleError<std::convert::Infallible> {
fn into_conversion_error<E: std::fmt::Debug>(self) -> ScriptModuleCallHandleError<E> {
match self {
ScriptModuleCallHandleError::TypeMismatch { expected, actual } => {
ScriptModuleCallHandleError::TypeMismatch { expected, actual }
}
ScriptModuleCallHandleError::KeyContainsNullByte(error) => {
ScriptModuleCallHandleError::KeyContainsNullByte(error)
}
ScriptModuleCallHandleError::ValueContainsNullByte(error) => {
ScriptModuleCallHandleError::ValueContainsNullByte(error)
}
ScriptModuleCallHandleError::TooManyElements => {
ScriptModuleCallHandleError::TooManyElements
}
ScriptModuleCallHandleError::ConversionError(error) => match error {},
}
}
}
pub trait FromScriptModuleInteger: Sized {
type Error: std::fmt::Debug;
fn from_script_module_integer(value: i32) -> Result<Self, Self::Error>;
}
#[duplicate::duplicate_item(
Integer;
[i8];
[i16];
[isize];
[u8];
[u16];
[u32];
[u64];
[u128];
[usize];
)]
impl FromScriptModuleInteger for Integer {
type Error = <Self as TryFrom<i32>>::Error;
fn from_script_module_integer(value: i32) -> Result<Self, Self::Error> {
value.try_into()
}
}
impl FromScriptModuleInteger for i32 {
type Error = std::convert::Infallible;
fn from_script_module_integer(value: i32) -> Result<Self, Self::Error> {
Ok(value)
}
}
#[duplicate::duplicate_item(
Integer;
[i64];
[i128];
)]
impl FromScriptModuleInteger for Integer {
type Error = std::convert::Infallible;
fn from_script_module_integer(value: i32) -> Result<Self, Self::Error> {
Ok(value.into())
}
}
fn convert_script_module_integer<T>(value: i32) -> ScriptModuleCallHandleResult<T, T::Error>
where
T: FromScriptModuleInteger,
{
T::from_script_module_integer(value).map_err(ScriptModuleCallHandleError::ConversionError)
}
#[derive(Debug, Clone, Copy)]
pub struct ScriptModuleFunctionCallback {
pub func: unsafe extern "C" fn(*mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM),
pub userdata: *mut std::ffi::c_void,
}
pub struct ScriptModuleUserData<T: Send + Sync + 'static + AsScriptModuleUserData> {
data: std::sync::Arc<std::sync::Mutex<T>>,
}
struct MetaMethodFunctionTableEntry {
meta_method_functions: Box<[aviutl2_sys::module2::META_METHOD_FUNCTION]>,
}
unsafe impl Send for MetaMethodFunctionTableEntry {}
unsafe impl Sync for MetaMethodFunctionTableEntry {}
fn type_to_meta_method_functions<T: Send + Sync + 'static + AsScriptModuleUserData>()
-> *const aviutl2_sys::module2::META_METHOD_FUNCTION {
static META_METHOD_FUNCTIONS: std::sync::LazyLock<
dashmap::DashMap<std::any::TypeId, MetaMethodFunctionTableEntry>,
> = std::sync::LazyLock::new(Default::default);
let type_id = std::any::TypeId::of::<T>();
META_METHOD_FUNCTIONS
.entry(type_id)
.or_insert_with(|| {
let last = T::META_METHOD_FUNCTIONS.last();
assert!(
last.is_some() && last.unwrap().method.is_null(),
"META_METHOD_FUNCTIONS must be null-terminated"
);
MetaMethodFunctionTableEntry {
meta_method_functions: T::META_METHOD_FUNCTIONS.to_vec().into_boxed_slice(),
}
})
.meta_method_functions
.as_ptr()
}
#[derive(Debug)]
pub struct ErasedScriptModuleUserData {
pub meta_method_functions: *const aviutl2_sys::module2::META_METHOD_FUNCTION,
pub userdata: *mut std::ffi::c_void,
}
impl<T: Send + Sync + 'static + AsScriptModuleUserData> From<ScriptModuleUserData<T>>
for ErasedScriptModuleUserData
{
fn from(meta_table: ScriptModuleUserData<T>) -> Self {
let data = Box::into_raw(Box::new(meta_table.data)) as *mut std::ffi::c_void;
let meta_method_functions = type_to_meta_method_functions::<T>();
ErasedScriptModuleUserData {
meta_method_functions,
userdata: data,
}
}
}
impl<T: Send + Sync + 'static + AsScriptModuleUserData> ScriptModuleUserData<T> {
pub fn new(data: T) -> Self {
ScriptModuleUserData {
data: std::sync::Arc::new(std::sync::Mutex::new(data)),
}
}
pub fn lock(
&self,
) -> Result<std::sync::MutexGuard<'_, T>, std::sync::PoisonError<std::sync::MutexGuard<'_, T>>>
{
self.data.lock()
}
}
impl<T: Send + Sync + 'static + AsScriptModuleUserData> From<T> for ScriptModuleUserData<T> {
fn from(data: T) -> Self {
ScriptModuleUserData::new(data)
}
}
unsafe extern "C" fn dummy_meta_method_function(
_smp: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
) {
}
unsafe extern "C" fn script_module_user_data_gc<
T: Send + Sync + 'static + AsScriptModuleUserData,
>(
smp: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
) {
let userdata = unsafe { (*smp).userdata };
if !userdata.is_null() {
unsafe {
drop(Box::<std::sync::Arc<std::sync::Mutex<T>>>::from_raw(
userdata as *mut std::sync::Arc<std::sync::Mutex<T>>,
));
}
}
}
pub trait AsScriptModuleUserData: Send + Sync + Sized + 'static {
const META_METHOD_FUNCTIONS: &'static [aviutl2_sys::module2::META_METHOD_FUNCTION] = &[
aviutl2_sys::module2::META_METHOD_FUNCTION {
method: c"__gc".as_ptr(),
func: script_module_user_data_gc::<Self>,
},
aviutl2_sys::module2::META_METHOD_FUNCTION {
method: std::ptr::null(),
func: dummy_meta_method_function,
},
];
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParamType {
Nil,
Boolean,
LightUserdata,
Number,
String,
Table,
Function,
Userdata,
Thread,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum GetParamError<T: std::fmt::Debug> {
#[error("expected {expected:?}, but got {actual:?}")]
TypeMismatch {
expected: ParamType,
actual: ParamType,
},
#[error("index {index} is out of bounds, {len} given")]
IndexOutOfBounds {
index: usize,
len: usize,
},
#[error("failed to convert value: {0}")]
ConversionError(T),
}
pub type GetParamResult<T, E = std::convert::Infallible> = std::result::Result<T, GetParamError<E>>;
#[derive(Debug, thiserror::Error)]
#[error("{message}")]
pub struct ParamConversionError {
message: String,
}
impl ParamConversionError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
}
impl GetParamError<std::convert::Infallible> {
fn into_conversion_error<T: std::fmt::Debug>(self) -> GetParamError<T> {
match self {
GetParamError::TypeMismatch { expected, actual } => {
GetParamError::TypeMismatch { expected, actual }
}
GetParamError::IndexOutOfBounds { index, len } => {
GetParamError::IndexOutOfBounds { index, len }
}
GetParamError::ConversionError(error) => match error {},
}
}
}
impl ScriptModuleCallHandle {
pub unsafe fn from_raw(
ptr: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
) -> ScriptModuleCallHandle {
ScriptModuleCallHandle {
internal: ptr,
read_section: unsafe { crate::generic::ReadSection::from_raw((*ptr).edit) },
}
}
pub fn len(&self) -> usize {
unsafe { ((*self.internal).get_param_num)() as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get_param_type(&self, index: usize) -> Option<ParamType> {
let param_type = unsafe { ((*self.internal).get_param_type)(index as i32) };
match param_type {
aviutl2_sys::module2::PARAM_TYPE::NONE => None,
aviutl2_sys::module2::PARAM_TYPE::NIL => Some(ParamType::Nil),
aviutl2_sys::module2::PARAM_TYPE::BOOLEAN => Some(ParamType::Boolean),
aviutl2_sys::module2::PARAM_TYPE::LIGHTUSERDATA => Some(ParamType::LightUserdata),
aviutl2_sys::module2::PARAM_TYPE::NUMBER => Some(ParamType::Number),
aviutl2_sys::module2::PARAM_TYPE::STRING => Some(ParamType::String),
aviutl2_sys::module2::PARAM_TYPE::TABLE => Some(ParamType::Table),
aviutl2_sys::module2::PARAM_TYPE::FUNCTION => Some(ParamType::Function),
aviutl2_sys::module2::PARAM_TYPE::USERDATA => Some(ParamType::Userdata),
aviutl2_sys::module2::PARAM_TYPE::THREAD => Some(ParamType::Thread),
}
}
fn assert_param_type(&self, index: usize, expected: ParamType) -> GetParamResult<()> {
let actual = self
.get_param_type(index)
.ok_or(GetParamError::IndexOutOfBounds {
index,
len: self.len(),
})?;
if actual != expected {
return Err(GetParamError::TypeMismatch { expected, actual });
}
Ok(())
}
fn assert_param_type_for_call(
&self,
index: usize,
expected: ParamType,
) -> ScriptModuleCallHandleResult<()> {
let Some(actual) = self.get_param_type(index) else {
return Ok(());
};
if actual != expected {
return Err(ScriptModuleCallHandleError::TypeMismatch { expected, actual });
}
Ok(())
}
pub fn get_param<'a, T: FromScriptModuleParam<'a>>(
&'a self,
index: usize,
) -> GetParamResult<T, T::Error> {
T::from_param(self, index)
}
pub fn get_param_int(&self, index: usize) -> GetParamResult<i32> {
self.assert_param_type(index, ParamType::Number)?;
Ok(unsafe { ((*self.internal).get_param_int)(index as i32) })
}
pub fn get_param_float(&self, index: usize) -> GetParamResult<f64> {
self.assert_param_type(index, ParamType::Number)?;
Ok(unsafe { ((*self.internal).get_param_double)(index as i32) })
}
pub fn get_param_str(&self, index: usize) -> GetParamResult<String> {
self.assert_param_type(index, ParamType::String)?;
unsafe {
let c_str = ((*self.internal).get_param_string)(index as i32);
assert!(!c_str.is_null(), "get_param_string returned null");
Ok(std::ffi::CStr::from_ptr(c_str)
.to_string_lossy()
.into_owned())
}
}
pub fn get_param_data<T>(&self, index: usize) -> GetParamResult<*mut T> {
let param_type = self
.get_param_type(index)
.ok_or(GetParamError::IndexOutOfBounds {
index,
len: self.len(),
})?;
if param_type != ParamType::LightUserdata && param_type != ParamType::Userdata {
return Err(GetParamError::TypeMismatch {
expected: ParamType::LightUserdata,
actual: param_type,
});
}
unsafe { Ok(((*self.internal).get_param_data)(index as i32) as *mut T) }
}
pub fn get_param_userdata<T: AsScriptModuleUserData>(
&self,
index: usize,
) -> GetParamResult<ScriptModuleUserData<T>, ParamConversionError> {
self.assert_param_type(index, ParamType::Userdata)
.map_err(GetParamError::into_conversion_error)?;
let ptr = unsafe {
((*self.internal).get_param_meta_table)(
index as i32,
type_to_meta_method_functions::<T>() as _,
)
};
if ptr.is_null() {
return Err(GetParamError::ConversionError(ParamConversionError::new(
"userdata type mismatch",
)));
}
let arc_ref = unsafe {
let boxed = &*(ptr as *const std::sync::Arc<std::sync::Mutex<T>>);
boxed.clone()
};
Ok(ScriptModuleUserData { data: arc_ref })
}
pub fn get_param_boolean(&self, index: usize) -> GetParamResult<bool> {
self.assert_param_type(index, ParamType::Boolean)?;
unsafe { Ok(((*self.internal).get_param_boolean)(index as i32)) }
}
pub fn get_param_table_int(
&self,
index: usize,
key: &str,
) -> ScriptModuleCallHandleResult<i32> {
self.assert_param_type_for_call(index, ParamType::Table)?;
let c_key = std::ffi::CString::new(key)
.map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
Ok(unsafe { ((*self.internal).get_param_table_int)(index as i32, c_key.as_ptr()) })
}
pub fn get_param_table_integer<T>(
&self,
index: usize,
key: &str,
) -> ScriptModuleCallHandleResult<T, T::Error>
where
T: FromScriptModuleInteger,
{
let value = self
.get_param_table_int(index, key)
.map_err(ScriptModuleCallHandleError::into_conversion_error)?;
convert_script_module_integer(value)
}
pub fn get_param_table_float(
&self,
index: usize,
key: &str,
) -> ScriptModuleCallHandleResult<f64> {
self.assert_param_type_for_call(index, ParamType::Table)?;
let c_key = std::ffi::CString::new(key)
.map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
Ok(unsafe { ((*self.internal).get_param_table_double)(index as i32, c_key.as_ptr()) })
}
pub fn get_param_table_str(
&self,
index: usize,
key: &str,
) -> ScriptModuleCallHandleResult<Option<String>> {
self.assert_param_type_for_call(index, ParamType::Table)?;
let c_key = std::ffi::CString::new(key)
.map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
unsafe {
let c_str = ((*self.internal).get_param_table_string)(index as i32, c_key.as_ptr());
Ok(if c_str.is_null() {
None
} else {
Some(
std::ffi::CStr::from_ptr(c_str)
.to_string_lossy()
.into_owned(),
)
})
}
}
pub fn get_param_table_boolean(
&self,
index: usize,
key: &str,
) -> ScriptModuleCallHandleResult<bool> {
self.assert_param_type_for_call(index, ParamType::Table)?;
let c_key = std::ffi::CString::new(key)
.map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
Ok(unsafe { ((*self.internal).get_param_table_boolean)(index as i32, c_key.as_ptr()) })
}
pub fn get_param_array_len(&self, index: usize) -> usize {
unsafe { ((*self.internal).get_param_array_num)(index as i32) as usize }
}
pub fn get_param_array_int(&self, index: usize, array_index: usize) -> i32 {
unsafe { ((*self.internal).get_param_array_int)(index as i32, array_index as i32) }
}
pub fn get_param_array_float(&self, index: usize, array_index: usize) -> f64 {
unsafe { ((*self.internal).get_param_array_double)(index as i32, array_index as i32) }
}
pub fn get_param_array_str(&self, index: usize, array_index: usize) -> Option<String> {
unsafe {
let c_str = ((*self.internal).get_param_array_string)(index as i32, array_index as i32);
if c_str.is_null() {
None
} else {
Some(
std::ffi::CStr::from_ptr(c_str)
.to_string_lossy()
.into_owned(),
)
}
}
}
pub fn set_error(&mut self, message: &str) -> ScriptModuleCallHandleResult<()> {
let c_message = std::ffi::CString::new(message)
.map_err(ScriptModuleCallHandleError::ValueContainsNullByte)?;
unsafe {
((*self.internal).set_error)(c_message.as_ptr());
}
Ok(())
}
pub fn push_result<T: IntoScriptModuleReturnValue>(
&mut self,
value: T,
) -> Result<(), IntoScriptModuleReturnValueError<T::Err>> {
value.push_into(self)
}
pub fn push_result_int(&mut self, value: i32) {
unsafe {
((*self.internal).push_result_int)(value);
}
}
pub fn push_result_float(&mut self, value: f64) {
unsafe {
((*self.internal).push_result_double)(value);
}
}
pub fn push_result_str(&mut self, value: &str) -> ScriptModuleCallHandleResult<()> {
let c_value = std::ffi::CString::new(value)
.map_err(ScriptModuleCallHandleError::ValueContainsNullByte)?;
unsafe {
((*self.internal).push_result_string)(c_value.as_ptr());
}
Ok(())
}
pub fn push_result_data<T>(&mut self, value: *const T) {
unsafe {
((*self.internal).push_result_data)(value as *const std::ffi::c_void);
}
}
pub fn push_result_function(&mut self, callback: ScriptModuleFunctionCallback) {
unsafe {
((*self.internal).push_result_function)(callback.func, callback.userdata);
}
}
pub fn push_result_meta_table<T: Into<ErasedScriptModuleUserData>>(&mut self, meta_table: T) {
unsafe {
let meta_table: ErasedScriptModuleUserData = meta_table.into();
((*self.internal).push_result_meta_table)(
meta_table.meta_method_functions,
meta_table.userdata,
);
}
}
pub fn push_result_table_int<'a, T>(&mut self, table: T) -> ScriptModuleCallHandleResult<()>
where
T: std::iter::IntoIterator<Item = (&'a str, i32)>,
{
let mut keys = Vec::new();
let mut values = Vec::new();
for (key, value) in table {
let c_key = std::ffi::CString::new(key)
.map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
keys.push(c_key);
values.push(value);
}
let key_ptrs: Vec<*const std::os::raw::c_char> = keys.iter().map(|k| k.as_ptr()).collect();
unsafe {
((*self.internal).push_result_table_int)(
key_ptrs.as_ptr(),
values.as_ptr(),
key_ptrs.len() as i32,
);
}
Ok(())
}
pub fn push_result_table_float<'a, T>(&mut self, table: T) -> ScriptModuleCallHandleResult<()>
where
T: std::iter::IntoIterator<Item = (&'a str, f64)>,
{
let mut keys = Vec::new();
let mut values = Vec::new();
for (key, value) in table {
let c_key = std::ffi::CString::new(key)
.map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
keys.push(c_key);
values.push(value);
}
let key_ptrs: Vec<*const std::os::raw::c_char> = keys.iter().map(|k| k.as_ptr()).collect();
unsafe {
((*self.internal).push_result_table_double)(
key_ptrs.as_ptr(),
values.as_ptr(),
key_ptrs.len() as i32,
);
}
Ok(())
}
pub fn push_result_table_str<'a, T>(&mut self, table: T) -> ScriptModuleCallHandleResult<()>
where
T: std::iter::IntoIterator<Item = (&'a str, &'a str)>,
{
let mut keys = Vec::new();
let mut values = Vec::new();
for (key, value) in table {
let c_key = std::ffi::CString::new(key)
.map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
let c_value = std::ffi::CString::new(value)
.map_err(ScriptModuleCallHandleError::ValueContainsNullByte)?;
keys.push(c_key);
values.push(c_value);
}
if keys.len() > i32::MAX as usize {
return Err(ScriptModuleCallHandleError::TooManyElements);
}
let key_ptrs: Vec<*const std::os::raw::c_char> = keys.iter().map(|k| k.as_ptr()).collect();
let value_ptrs: Vec<*const std::os::raw::c_char> =
values.iter().map(|v| v.as_ptr()).collect();
unsafe {
((*self.internal).push_result_table_string)(
key_ptrs.as_ptr(),
value_ptrs.as_ptr(),
key_ptrs.len() as i32,
);
}
Ok(())
}
pub fn push_result_table_boolean<'a, T>(&mut self, table: T) -> ScriptModuleCallHandleResult<()>
where
T: std::iter::IntoIterator<Item = (&'a str, bool)>,
{
let mut keys = Vec::new();
let mut values = Vec::new();
for (key, value) in table {
let c_key = std::ffi::CString::new(key)
.map_err(ScriptModuleCallHandleError::KeyContainsNullByte)?;
keys.push(c_key);
values.push(value);
}
let key_ptrs: Vec<*const std::os::raw::c_char> = keys.iter().map(|k| k.as_ptr()).collect();
unsafe {
((*self.internal).push_result_table_boolean)(
key_ptrs.as_ptr(),
values.as_ptr(),
key_ptrs.len() as i32,
);
}
Ok(())
}
pub fn push_result_array_int(&mut self, values: &[i32]) -> ScriptModuleCallHandleResult<()> {
if values.len() > i32::MAX as usize {
return Err(ScriptModuleCallHandleError::TooManyElements);
}
unsafe {
((*self.internal).push_result_array_int)(values.as_ptr(), values.len() as i32);
}
Ok(())
}
pub fn push_result_array_float(&mut self, values: &[f64]) -> ScriptModuleCallHandleResult<()> {
if values.len() > i32::MAX as usize {
return Err(ScriptModuleCallHandleError::TooManyElements);
}
unsafe {
((*self.internal).push_result_array_double)(values.as_ptr(), values.len() as i32);
}
Ok(())
}
pub fn push_result_array_str(&mut self, values: &[&str]) -> ScriptModuleCallHandleResult<()> {
let c_values: Vec<std::ffi::CString> = values
.iter()
.map(|s| std::ffi::CString::new(*s))
.collect::<Result<_, _>>()
.map_err(ScriptModuleCallHandleError::ValueContainsNullByte)?;
if c_values.len() > i32::MAX as usize {
return Err(ScriptModuleCallHandleError::TooManyElements);
}
let c_value_ptrs: Vec<*const std::os::raw::c_char> =
c_values.iter().map(|s| s.as_ptr()).collect();
unsafe {
((*self.internal).push_result_array_string)(
c_value_ptrs.as_ptr(),
c_value_ptrs.len() as i32,
);
}
Ok(())
}
pub fn push_result_array_boolean(
&mut self,
values: &[bool],
) -> ScriptModuleCallHandleResult<()> {
if values.len() > i32::MAX as usize {
return Err(ScriptModuleCallHandleError::TooManyElements);
}
unsafe {
((*self.internal).push_result_array_boolean)(values.as_ptr(), values.len() as i32);
}
Ok(())
}
pub fn push_result_boolean(&mut self, value: bool) {
unsafe {
((*self.internal).push_result_boolean)(value);
}
}
pub fn read_section(&self) -> &crate::generic::ReadSection {
&self.read_section
}
}
pub trait FromScriptModuleParam<'a>: Sized {
type Error: std::error::Error;
fn from_param(
param: &'a crate::module::ScriptModuleCallHandle,
index: usize,
) -> GetParamResult<Self, Self::Error>;
}
pub use aviutl2_macros::FromScriptModuleParam;
impl<'a> FromScriptModuleParam<'a> for &'a crate::generic::ReadSection {
type Error = std::convert::Infallible;
fn from_param(
param: &'a crate::module::ScriptModuleCallHandle,
_index: usize,
) -> GetParamResult<Self> {
Ok(param.read_section())
}
}
impl<'a> FromScriptModuleParam<'a> for i32 {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
param.get_param_int(index)
}
}
#[duplicate::duplicate_item(
Integer Failable;
[i8] [true];
[i16] [true];
[i64] [false];
[i128] [false];
[isize] [false];
[u8] [true];
[u16] [true];
[u32] [true];
[u64] [false];
[u128] [false];
[usize] [false];
)]
impl<'a> FromScriptModuleParam<'a> for Integer {
type Error = std::num::TryFromIntError;
fn from_param(
param: &'a ScriptModuleCallHandle,
index: usize,
) -> GetParamResult<Self, Self::Error> {
let value = param
.get_param_int(index)
.map_err(GetParamError::into_conversion_error)?;
comptime_if::comptime_if!(
if failable where (failable = Failable) {
value.try_into().map_err(GetParamError::ConversionError)
} else {
Ok(value as Integer)
}
)
}
}
impl<'a> FromScriptModuleParam<'a> for f64 {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
param.get_param_float(index)
}
}
impl<'a> FromScriptModuleParam<'a> for f32 {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
param.get_param_float(index).map(|value| value as f32)
}
}
impl<'a> FromScriptModuleParam<'a> for bool {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
param.get_param_boolean(index)
}
}
impl<'a> FromScriptModuleParam<'a> for String {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
param.get_param_str(index)
}
}
impl<'a, T> FromScriptModuleParam<'a> for *mut T {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
param.get_param_data(index)
}
}
impl<'a, T> FromScriptModuleParam<'a> for *const T {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
Ok(param.get_param_data(index)? as _)
}
}
impl<'a, T> FromScriptModuleParam<'a> for NonNull<T> {
type Error = ParamConversionError;
fn from_param(
param: &'a ScriptModuleCallHandle,
index: usize,
) -> GetParamResult<Self, Self::Error> {
let ptr = param
.get_param_data(index)
.map_err(GetParamError::into_conversion_error)?;
NonNull::new(ptr).ok_or_else(|| {
GetParamError::ConversionError(ParamConversionError::new("value is null"))
})
}
}
impl<'a, T: Send + Sync + 'static + AsScriptModuleUserData> FromScriptModuleParam<'a>
for ScriptModuleUserData<T>
{
type Error = ParamConversionError;
fn from_param(
param: &'a ScriptModuleCallHandle,
index: usize,
) -> GetParamResult<Self, Self::Error> {
param.get_param_userdata(index)
}
}
#[duplicate::duplicate_item(
Integer NonZero Failable;
[i8] [NonZeroI8] [true];
[i16] [NonZeroI16] [true];
[i64] [NonZeroI64] [false];
[i128] [NonZeroI128] [false];
[isize] [NonZeroIsize] [false];
[u8] [NonZeroU8] [true];
[u16] [NonZeroU16] [true];
[u32] [NonZeroU32] [true];
[u64] [NonZeroU64] [false];
[u128] [NonZeroU128] [false];
[usize] [NonZeroUsize] [false];
)]
impl<'a> FromScriptModuleParam<'a> for NonZero {
type Error = ParamConversionError;
fn from_param(
param: &'a ScriptModuleCallHandle,
index: usize,
) -> GetParamResult<Self, Self::Error> {
let value = param
.get_param_int(index)
.map_err(GetParamError::into_conversion_error)?;
let value: Integer = comptime_if::comptime_if!(
if failable where (failable = Failable) {
value
.try_into()
.map_err(|error: std::num::TryFromIntError| GetParamError::ConversionError(ParamConversionError::new(error.to_string())))?
} else {
value as Integer
}
);
NonZero::new(value).ok_or_else(|| {
GetParamError::ConversionError(ParamConversionError::new("value is zero"))
})
}
}
impl<'a> FromScriptModuleParam<'a> for NonZeroI32 {
type Error = ParamConversionError;
fn from_param(
param: &'a ScriptModuleCallHandle,
index: usize,
) -> GetParamResult<Self, Self::Error> {
let value = param
.get_param_int(index)
.map_err(GetParamError::into_conversion_error)?;
NonZeroI32::new(value).ok_or_else(|| {
GetParamError::ConversionError(ParamConversionError::new("value is zero"))
})
}
}
impl<'a, T> FromScriptModuleParam<'a> for Option<T>
where
T: FromScriptModuleParam<'a>,
{
type Error = T::Error;
fn from_param(
param: &'a ScriptModuleCallHandle,
index: usize,
) -> GetParamResult<Self, Self::Error> {
match param.get_param_type(index) {
None | Some(ParamType::Nil) => Ok(None),
Some(_) => T::from_param(param, index).map(Some),
}
}
}
impl<'a> FromScriptModuleParam<'a> for () {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
param
.get_param_type(index)
.map(|_| ())
.ok_or(GetParamError::IndexOutOfBounds {
index,
len: param.len(),
})
}
}
pub struct ScriptModuleParamArray<'a> {
index: usize,
ptr: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
marker: std::marker::PhantomData<&'a ()>,
}
impl std::fmt::Debug for ScriptModuleParamArray<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScriptModuleParamArray")
.field("index", &self.index)
.field("len", &self.len())
.finish()
}
}
impl<'a> ScriptModuleParamArray<'a> {
pub fn len(&self) -> usize {
unsafe { ((*self.ptr).get_param_array_num)(self.index as i32) as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get_int(&self, array_index: usize) -> i32 {
unsafe { ((*self.ptr).get_param_array_int)(self.index as i32, array_index as i32) }
}
pub fn get_float(&self, array_index: usize) -> f64 {
unsafe { ((*self.ptr).get_param_array_double)(self.index as i32, array_index as i32) }
}
pub fn get_str(&self, array_index: usize) -> Option<String> {
unsafe {
let c_str = ((*self.ptr).get_param_array_string)(self.index as i32, array_index as i32);
if c_str.is_null() {
None
} else {
Some(
std::ffi::CStr::from_ptr(c_str)
.to_string_lossy()
.into_owned(),
)
}
}
}
}
impl<'a> FromScriptModuleParam<'a> for ScriptModuleParamArray<'a> {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
param.assert_param_type(index, ParamType::Table)?;
Ok(ScriptModuleParamArray {
index,
ptr: param.internal,
marker: std::marker::PhantomData,
})
}
}
#[derive(Debug)]
pub struct ScriptModuleParamTable<'a> {
index: usize,
ptr: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM,
marker: std::marker::PhantomData<&'a ()>,
}
impl<'a> ScriptModuleParamTable<'a> {
pub fn get_int(&self, key: &str) -> i32 {
let c_key = std::ffi::CString::new(key).unwrap();
unsafe { ((*self.ptr).get_param_table_int)(self.index as i32, c_key.as_ptr()) }
}
pub fn get_float(&self, key: &str) -> f64 {
let c_key = std::ffi::CString::new(key).unwrap();
unsafe { ((*self.ptr).get_param_table_double)(self.index as i32, c_key.as_ptr()) }
}
pub fn get_str(&self, key: &str) -> Option<String> {
let c_key = std::ffi::CString::new(key).unwrap();
unsafe {
let c_str = ((*self.ptr).get_param_table_string)(self.index as i32, c_key.as_ptr());
if c_str.is_null() {
None
} else {
Some(
std::ffi::CStr::from_ptr(c_str)
.to_string_lossy()
.into_owned(),
)
}
}
}
pub fn get_boolean(&self, key: &str) -> bool {
let c_key = std::ffi::CString::new(key).unwrap();
unsafe { ((*self.ptr).get_param_table_boolean)(self.index as i32, c_key.as_ptr()) }
}
}
impl<'a> FromScriptModuleParam<'a> for ScriptModuleParamTable<'a> {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
param.assert_param_type(index, ParamType::Table)?;
Ok(ScriptModuleParamTable {
index,
ptr: param.internal,
marker: std::marker::PhantomData,
})
}
}
impl<'a> FromScriptModuleParam<'a> for Vec<String> {
type Error = ParamConversionError;
fn from_param(
param: &'a ScriptModuleCallHandle,
index: usize,
) -> GetParamResult<Self, Self::Error> {
let array = ScriptModuleParamArray::from_param(param, index)
.map_err(GetParamError::into_conversion_error)?;
let mut result = Vec::new();
for i in 0..array.len() {
let value = array.get_str(i).ok_or_else(|| {
GetParamError::ConversionError(ParamConversionError::new(format!(
"array element #{i} is not a string"
)))
})?;
result.push(value);
}
Ok(result)
}
}
impl<'a> FromScriptModuleParam<'a> for Vec<i32> {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
let array = ScriptModuleParamArray::from_param(param, index)?;
let mut result = Vec::new();
for i in 0..array.len() {
result.push(array.get_int(i));
}
Ok(result)
}
}
#[duplicate::duplicate_item(
Integer Failable;
[i8] [true];
[i16] [true];
[i64] [false];
[i128] [false];
[isize] [false];
[u8] [true];
[u16] [true];
[u32] [true];
[u64] [true];
[u128] [true];
[usize] [true];
)]
impl<'a> FromScriptModuleParam<'a> for Vec<Integer> {
type Error = std::num::TryFromIntError;
fn from_param(
param: &'a ScriptModuleCallHandle,
index: usize,
) -> GetParamResult<Self, Self::Error> {
let array = ScriptModuleParamArray::from_param(param, index)
.map_err(GetParamError::into_conversion_error)?;
let mut result = Vec::new();
for i in 0..array.len() {
let value = array.get_int(i);
comptime_if::comptime_if!(
if failable where (failable = Failable) {
result.push(value.try_into().map_err(GetParamError::ConversionError)?);
} else {
result.push(value as Integer);
}
);
}
Ok(result)
}
}
impl<'a> FromScriptModuleParam<'a> for Vec<f64> {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
let array = ScriptModuleParamArray::from_param(param, index)?;
let mut result = Vec::new();
for i in 0..array.len() {
result.push(array.get_float(i));
}
Ok(result)
}
}
impl<'a> FromScriptModuleParam<'a> for Vec<f32> {
type Error = std::convert::Infallible;
fn from_param(param: &'a ScriptModuleCallHandle, index: usize) -> GetParamResult<Self> {
let array = ScriptModuleParamArray::from_param(param, index)?;
let mut result = Vec::new();
for i in 0..array.len() {
result.push(array.get_float(i) as f32);
}
Ok(result)
}
}
pub trait FromScriptModuleParamTable<'a>: Sized {
type Error: std::error::Error;
fn from_param_table(
param: &'a crate::module::ScriptModuleParamTable,
key: &str,
) -> GetParamResult<Self, Self::Error>;
}
impl<'a> FromScriptModuleParamTable<'a> for i32 {
type Error = std::convert::Infallible;
fn from_param_table(param: &'a ScriptModuleParamTable, key: &str) -> GetParamResult<Self> {
Ok(param.get_int(key))
}
}
#[duplicate::duplicate_item(
Integer Failable;
[i8] [true];
[i16] [true];
[i64] [false];
[i128] [false];
[isize] [false];
[u8] [true];
[u16] [true];
[u32] [true];
[u64] [false];
[u128] [false];
[usize] [false];
)]
impl<'a> FromScriptModuleParamTable<'a> for Integer {
type Error = std::num::TryFromIntError;
fn from_param_table(
param: &'a ScriptModuleParamTable,
key: &str,
) -> GetParamResult<Self, Self::Error> {
let value = param.get_int(key);
comptime_if::comptime_if!(
if failable where (failable = Failable) {
value.try_into().map_err(GetParamError::ConversionError)
} else {
Ok(value as Integer)
}
)
}
}
impl<'a> FromScriptModuleParamTable<'a> for f64 {
type Error = std::convert::Infallible;
fn from_param_table(param: &'a ScriptModuleParamTable, key: &str) -> GetParamResult<Self> {
Ok(param.get_float(key))
}
}
impl<'a> FromScriptModuleParamTable<'a> for f32 {
type Error = std::convert::Infallible;
fn from_param_table(param: &'a ScriptModuleParamTable, key: &str) -> GetParamResult<Self> {
Ok(param.get_float(key) as f32)
}
}
impl<'a> FromScriptModuleParamTable<'a> for String {
type Error = ParamConversionError;
fn from_param_table(
param: &'a ScriptModuleParamTable,
key: &str,
) -> GetParamResult<Self, Self::Error> {
param.get_str(key).ok_or_else(|| {
GetParamError::ConversionError(ParamConversionError::new(format!(
"key `{key}` is not a string"
)))
})
}
}
impl<'a> FromScriptModuleParamTable<'a> for bool {
type Error = std::convert::Infallible;
fn from_param_table(param: &'a ScriptModuleParamTable, key: &str) -> GetParamResult<Self> {
Ok(param.get_boolean(key))
}
}
impl<'a, T: FromScriptModuleParamTable<'a>> FromScriptModuleParamTable<'a> for Option<T> {
type Error = T::Error;
fn from_param_table(
param: &'a ScriptModuleParamTable,
key: &str,
) -> GetParamResult<Self, Self::Error> {
match T::from_param_table(param, key) {
Ok(value) => Ok(Some(value)),
Err(GetParamError::ConversionError(_)) => Ok(None),
Err(error) => Err(error),
}
}
}
#[derive(Debug)]
pub enum ScriptModuleReturnValue {
Int(i32),
Float(f64),
String(String),
Boolean(bool),
Data(*const std::ffi::c_void),
StringArray(Vec<String>),
IntArray(Vec<i32>),
FloatArray(Vec<f64>),
IntTable(std::collections::HashMap<String, i32>),
FloatTable(std::collections::HashMap<String, f64>),
StringTable(std::collections::HashMap<String, String>),
Function(ScriptModuleFunctionCallback),
MetaTable(ErasedScriptModuleUserData),
}
#[derive(thiserror::Error, Debug)]
pub enum IntoScriptModuleReturnValueError<T> {
#[error("failed to convert value: {0}")]
ConversionFailed(#[source] T),
#[error("failed to push return value: {0}")]
PushFailed(#[from] ScriptModuleCallHandleError),
}
pub trait IntoScriptModuleReturnValue
where
Self: Sized,
{
type Err: Send + Sync + 'static + Into<Box<dyn std::error::Error + Send + Sync + 'static>>;
fn into_return_values(self) -> Result<Vec<crate::module::ScriptModuleReturnValue>, Self::Err>;
fn push_into(
self,
param: &mut crate::module::ScriptModuleCallHandle,
) -> Result<(), crate::module::IntoScriptModuleReturnValueError<Self::Err>> {
for value in self
.into_return_values()
.map_err(IntoScriptModuleReturnValueError::ConversionFailed)?
{
match value {
ScriptModuleReturnValue::Int(v) => {
param.push_result_int(v);
}
ScriptModuleReturnValue::Float(v) => {
param.push_result_float(v);
}
ScriptModuleReturnValue::String(v) => {
param.push_result_str(&v)?;
}
ScriptModuleReturnValue::Boolean(v) => {
param.push_result_boolean(v);
}
ScriptModuleReturnValue::Data(v) => {
param.push_result_data(v);
}
ScriptModuleReturnValue::Function(v) => {
param.push_result_function(v);
}
ScriptModuleReturnValue::MetaTable(v) => {
param.push_result_meta_table(v);
}
ScriptModuleReturnValue::StringArray(v) => {
let strs: Vec<&str> = v.iter().map(|s| s.as_str()).collect();
param.push_result_array_str(&strs)?
}
ScriptModuleReturnValue::IntArray(v) => param.push_result_array_int(&v)?,
ScriptModuleReturnValue::FloatArray(v) => param.push_result_array_float(&v)?,
ScriptModuleReturnValue::IntTable(v) => {
let table = v.iter().map(|(k, v)| (k.as_str(), *v));
param.push_result_table_int(table)?;
}
ScriptModuleReturnValue::FloatTable(v) => {
let table = v.iter().map(|(k, v)| (k.as_str(), *v));
param.push_result_table_float(table)?;
}
ScriptModuleReturnValue::StringTable(v) => {
let table = v.iter().map(|(k, v)| (k.as_str(), v.as_str()));
param.push_result_table_str(table)?;
}
};
}
Ok(())
}
}
pub use aviutl2_macros::IntoScriptModuleReturnValue;
impl<T> IntoScriptModuleReturnValue for *const T {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::Data(
self as *const std::ffi::c_void,
)])
}
}
impl IntoScriptModuleReturnValue for ScriptModuleFunctionCallback {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::Function(self)])
}
}
impl<T: Into<ErasedScriptModuleUserData>> IntoScriptModuleReturnValue for T {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::MetaTable(self.into())])
}
}
impl IntoScriptModuleReturnValue for i32 {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::Int(self)])
}
}
#[duplicate::duplicate_item(
Integer;
[i8];
[i16];
[u8];
[u16];
)]
impl IntoScriptModuleReturnValue for Integer {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::Int(self as i32)])
}
}
#[duplicate::duplicate_item(
Integer;
[i64];
[i128];
[isize];
[u32];
[u64];
[u128];
[usize];
)]
impl IntoScriptModuleReturnValue for Integer {
type Err = std::num::TryFromIntError;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::Int(self.try_into()?)])
}
}
impl IntoScriptModuleReturnValue for f64 {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::Float(self)])
}
}
impl IntoScriptModuleReturnValue for f32 {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::Float(self as f64)])
}
}
impl IntoScriptModuleReturnValue for bool {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::Boolean(self)])
}
}
impl IntoScriptModuleReturnValue for &str {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::String(self.to_string())])
}
}
impl IntoScriptModuleReturnValue for String {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::String(self)])
}
}
impl IntoScriptModuleReturnValue for ScriptModuleReturnValue {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![self])
}
}
impl IntoScriptModuleReturnValue for Vec<ScriptModuleReturnValue> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(self)
}
}
impl<T: IntoScriptModuleReturnValue> IntoScriptModuleReturnValue for Option<T> {
type Err = T::Err;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
if let Some(value) = self {
value.into_return_values()
} else {
Ok(Vec::new())
}
}
}
impl<T, const N: usize> IntoScriptModuleReturnValue for [T; N]
where
Vec<T>: IntoScriptModuleReturnValue,
{
type Err = <Vec<T> as IntoScriptModuleReturnValue>::Err;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
let vec: Vec<T> = self.into();
vec.into_return_values()
}
}
impl<T: IntoScriptModuleReturnValue, E> IntoScriptModuleReturnValue for Result<T, E>
where
E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
{
type Err = T::Err;
fn into_return_values(
self,
) -> Result<
Vec<ScriptModuleReturnValue>,
<std::result::Result<T, E> as IntoScriptModuleReturnValue>::Err,
> {
match self {
Ok(value) => value.into_return_values(),
Err(_) => Ok(Vec::new()),
}
}
fn push_into(
self,
param: &mut ScriptModuleCallHandle,
) -> Result<
(),
IntoScriptModuleReturnValueError<
<std::result::Result<T, E> as IntoScriptModuleReturnValue>::Err,
>,
> {
match self {
Ok(value) => value.push_into(param)?,
Err(err) => {
let e: Box<dyn std::error::Error + 'static> = err.into();
let e = e.to_string();
param.set_error(&e)?
}
}
Ok(())
}
}
impl IntoScriptModuleReturnValue for () {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(Vec::new())
}
}
macro_rules! impl_into_script_module_return_value_for_tuple {
($($name:ident),+) => {
impl<$($name),+> IntoScriptModuleReturnValue for ($($name,)+)
where
$($name: IntoScriptModuleReturnValue),+
{
type Err = anyhow::Error;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
let mut vec = Vec::new();
#[allow(non_snake_case)]
let ($($name,)+) = self;
$(
vec.extend(
$name.into_return_values()
.map_err(|e| anyhow::Error::from_boxed(e.into()))?
);
)+
Ok(vec)
}
}
};
}
impl_into_script_module_return_value_for_tuple!(T1);
impl_into_script_module_return_value_for_tuple!(T1, T2);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5, T6);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5, T6, T7);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_into_script_module_return_value_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl IntoScriptModuleReturnValue for Vec<String> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::StringArray(self)])
}
}
impl IntoScriptModuleReturnValue for Vec<&str> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::StringArray(
self.iter().map(|s| s.to_string()).collect(),
)])
}
}
impl IntoScriptModuleReturnValue for Vec<i32> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::IntArray(self)])
}
}
#[duplicate::duplicate_item(
Integer;
[i8];
[i16];
[u8];
[u16];
)]
impl IntoScriptModuleReturnValue for Vec<Integer> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::IntArray(
self.into_iter().map(|value| value as i32).collect(),
)])
}
}
#[duplicate::duplicate_item(
Integer;
[i64];
[i128];
[isize];
[u32];
[u64];
[u128];
[usize];
)]
impl IntoScriptModuleReturnValue for Vec<Integer> {
type Err = std::num::TryFromIntError;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::IntArray(
self.into_iter()
.map(i32::try_from)
.collect::<Result<_, _>>()?,
)])
}
}
impl IntoScriptModuleReturnValue for Vec<f64> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::FloatArray(self)])
}
}
impl IntoScriptModuleReturnValue for Vec<f32> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::FloatArray(
self.into_iter().map(f64::from).collect(),
)])
}
}
impl<T> IntoScriptModuleReturnValue for &[T]
where
Vec<T>: IntoScriptModuleReturnValue,
T: Clone,
{
type Err = <Vec<T> as IntoScriptModuleReturnValue>::Err;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
let vec: Vec<T> = self.to_vec();
vec.into_return_values()
}
}
impl IntoScriptModuleReturnValue for std::collections::HashMap<String, i32> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::IntTable(self)])
}
}
#[duplicate::duplicate_item(
Integer;
[i8];
[i16];
[u8];
[u16];
)]
impl IntoScriptModuleReturnValue for std::collections::HashMap<String, Integer> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::IntTable(
self.into_iter()
.map(|(key, value)| (key, value as i32))
.collect(),
)])
}
}
#[duplicate::duplicate_item(
Integer;
[i64];
[i128];
[isize];
[u32];
[u64];
[u128];
[usize];
)]
impl IntoScriptModuleReturnValue for std::collections::HashMap<String, Integer> {
type Err = std::num::TryFromIntError;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::IntTable(
self.into_iter()
.map(|(key, value)| Ok((key, value.try_into()?)))
.collect::<Result<_, Self::Err>>()?,
)])
}
}
impl IntoScriptModuleReturnValue for std::collections::HashMap<String, f64> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::FloatTable(self)])
}
}
impl IntoScriptModuleReturnValue for std::collections::HashMap<String, f32> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::FloatTable(
self.into_iter()
.map(|(key, value)| (key, f64::from(value)))
.collect(),
)])
}
}
impl IntoScriptModuleReturnValue for std::collections::HashMap<String, String> {
type Err = std::convert::Infallible;
fn into_return_values(self) -> Result<Vec<ScriptModuleReturnValue>, Self::Err> {
Ok(vec![ScriptModuleReturnValue::StringTable(self)])
}
}
#[doc(hidden)]
pub mod __table_converter {
pub trait ToOptionalTableEntry {
type Value;
fn to_optional(&self) -> Option<Self::Value>;
}
impl<T: Clone> ToOptionalTableEntry for Option<T> {
type Value = T;
fn to_optional(&self) -> Option<Self::Value> {
self.clone()
}
}
#[duplicate::duplicate_item(
Number;
[i8];
[i16];
[i32];
[i64];
[i128];
[isize];
[u8];
[u16];
[u32];
[u64];
[u128];
[usize];
[f32];
[f64];
)]
impl ToOptionalTableEntry for Number {
type Value = Number;
fn to_optional(&self) -> Option<Self::Value> {
Some(*self)
}
}
impl ToOptionalTableEntry for String {
type Value = String;
fn to_optional(&self) -> Option<Self::Value> {
Some(self.clone())
}
}
}
#[doc(hidden)]
pub fn __push_return_value<T>(param: &mut crate::module::ScriptModuleCallHandle, value: T)
where
T: crate::module::IntoScriptModuleReturnValue,
{
let res = value.push_into(param);
let _ = res
.map_err(|e| -> Box<dyn std::error::Error + Send + Sync + 'static> {
match e {
crate::module::IntoScriptModuleReturnValueError::PushFailed(e) => Box::new(e),
crate::module::IntoScriptModuleReturnValueError::ConversionFailed(e) => e.into(),
}
})
.push_into(param);
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_from_param<T>()
where
for<'a> T: FromScriptModuleParam<'a>,
{
}
fn assert_into_return_value<T: IntoScriptModuleReturnValue>() {}
fn assert_table_entry<T: __table_converter::ToOptionalTableEntry>() {}
fn assert_zero_is_convertible<T>()
where
T: FromScriptModuleInteger,
{
assert!(convert_script_module_integer::<T>(0).is_ok());
}
#[test]
fn converts_table_integer_to_all_numeric_types() {
assert_zero_is_convertible::<i8>();
assert_zero_is_convertible::<i16>();
assert_zero_is_convertible::<i32>();
assert_zero_is_convertible::<i64>();
assert_zero_is_convertible::<i128>();
assert_zero_is_convertible::<isize>();
assert_zero_is_convertible::<u8>();
assert_zero_is_convertible::<u16>();
assert_zero_is_convertible::<u32>();
assert_zero_is_convertible::<u64>();
assert_zero_is_convertible::<u128>();
assert_zero_is_convertible::<usize>();
}
#[test]
fn implements_numeric_collection_traits() {
macro_rules! assert_integer_traits {
($($integer:ty),+ $(,)?) => {
$(
assert_from_param::<Vec<$integer>>();
assert_into_return_value::<Vec<$integer>>();
assert_into_return_value::<std::collections::HashMap<String, $integer>>();
assert_table_entry::<$integer>();
)+
};
}
assert_integer_traits!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
assert_from_param::<Vec<f32>>();
assert_from_param::<Vec<f64>>();
assert_into_return_value::<Vec<f32>>();
assert_into_return_value::<Vec<f64>>();
assert_into_return_value::<std::collections::HashMap<String, f32>>();
assert_into_return_value::<std::collections::HashMap<String, f64>>();
assert_table_entry::<f32>();
assert_table_entry::<f64>();
}
#[test]
fn converts_numeric_collections_to_internal_types() {
let values = Vec::<u16>::from([0, u16::MAX])
.into_return_values()
.unwrap();
assert!(matches!(
values.as_slice(),
[ScriptModuleReturnValue::IntArray(values)]
if values == &vec![0, u16::MAX as i32]
));
let values = Vec::<f32>::from([1.5, -2.25]).into_return_values().unwrap();
assert!(matches!(
values.as_slice(),
[ScriptModuleReturnValue::FloatArray(values)]
if values == &vec![1.5, -2.25]
));
let values = std::collections::HashMap::from([("value".to_string(), 1.5_f32)])
.into_return_values()
.unwrap();
assert!(matches!(
values.as_slice(),
[ScriptModuleReturnValue::FloatTable(values)]
if values.get("value") == Some(&1.5)
));
}
#[test]
fn reports_numeric_collection_conversion_errors() {
assert!(
Vec::from([i64::from(i32::MAX) + 1])
.into_return_values()
.is_err()
);
assert!(
std::collections::HashMap::from([("value".to_string(), u64::MAX)])
.into_return_values()
.is_err()
);
}
#[test]
fn reports_table_integer_conversion_errors() {
assert!(matches!(
convert_script_module_integer::<i8>(i8::MAX as i32 + 1),
Err(ScriptModuleCallHandleError::ConversionError(_))
));
assert!(matches!(
convert_script_module_integer::<u32>(-1),
Err(ScriptModuleCallHandleError::ConversionError(_))
));
}
#[test]
fn preserves_existing_error_when_changing_conversion_error_type() {
let error: ScriptModuleCallHandleError<ParamConversionError> =
ScriptModuleCallHandleError::TypeMismatch {
expected: ParamType::Table,
actual: ParamType::Number,
}
.into_conversion_error();
assert!(matches!(
error,
ScriptModuleCallHandleError::TypeMismatch {
expected: ParamType::Table,
actual: ParamType::Number,
}
));
}
}