use std::borrow::Cow;
use std::marker::PhantomData;
use std::{fmt, ops};
use crate::core_types::{FromVariant, FromVariantError, Variant};
use crate::export::class::NativeClass;
use crate::export::{class_registry, ClassBuilder};
use crate::log::Site;
use crate::object::ownership::Shared;
use crate::object::{Ref, TInstance, TRef};
#[must_use = "MethodBuilder left unbuilt -- did you forget to call done() or done_stateless()?"]
pub struct MethodBuilder<'a, C, F> {
class_builder: &'a ClassBuilder<C>,
name: &'a str,
method: F,
rpc_mode: RpcMode,
}
impl<'a, C, F> MethodBuilder<'a, C, F>
where
C: NativeClass,
F: Method<C>,
{
pub(super) fn new(class_builder: &'a ClassBuilder<C>, name: &'a str, method: F) -> Self {
MethodBuilder {
class_builder,
name,
method,
rpc_mode: RpcMode::Disabled,
}
}
#[inline]
pub fn with_rpc_mode(mut self, rpc_mode: RpcMode) -> Self {
self.rpc_mode = rpc_mode;
self
}
#[inline]
pub fn done(self) {
let method_data = Box::into_raw(Box::new(self.method));
let script_method = ScriptMethod {
name: self.name,
method_ptr: Some(method_wrapper::<C, F>),
attributes: ScriptMethodAttributes {
rpc_mode: self.rpc_mode,
},
method_data: method_data as *mut libc::c_void,
free_func: Some(free_func::<F>),
};
self.class_builder.add_method(script_method);
}
}
impl<'a, C, F> MethodBuilder<'a, C, F>
where
C: NativeClass,
F: Method<C> + Copy + Default,
{
#[inline]
pub fn done_stateless(self) {
let script_method = ScriptMethod {
name: self.name,
method_ptr: Some(method_wrapper::<C, Stateless<F>>),
attributes: ScriptMethodAttributes {
rpc_mode: self.rpc_mode,
},
method_data: 1 as *mut libc::c_void,
free_func: None,
};
self.class_builder.add_method(script_method);
}
}
type ScriptMethodFn = unsafe extern "C" fn(
*mut sys::godot_object,
*mut libc::c_void,
*mut libc::c_void,
libc::c_int,
*mut *mut sys::godot_variant,
) -> sys::godot_variant;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum RpcMode {
Disabled,
Remote,
RemoteSync,
Master,
Puppet,
MasterSync,
PuppetSync,
}
impl Default for RpcMode {
#[inline]
fn default() -> Self {
RpcMode::Disabled
}
}
impl RpcMode {
pub(crate) fn sys(self) -> sys::godot_method_rpc_mode {
match self {
RpcMode::Master => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_MASTER,
RpcMode::Remote => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_REMOTE,
RpcMode::Puppet => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_PUPPET,
RpcMode::RemoteSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_REMOTESYNC,
RpcMode::Disabled => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_DISABLED,
RpcMode::MasterSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_MASTERSYNC,
RpcMode::PuppetSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_PUPPETSYNC,
}
}
}
pub(crate) struct ScriptMethodAttributes {
pub rpc_mode: RpcMode,
}
pub(crate) struct ScriptMethod<'l> {
pub name: &'l str,
pub method_ptr: Option<ScriptMethodFn>,
pub attributes: ScriptMethodAttributes,
pub method_data: *mut libc::c_void,
pub free_func: Option<unsafe extern "C" fn(*mut libc::c_void) -> ()>,
}
pub trait Method<C: NativeClass>: Send + Sync + 'static {
fn call(&self, this: TInstance<'_, C>, args: Varargs<'_>) -> Variant;
#[inline]
fn site() -> Option<Site<'static>> {
None
}
}
struct Stateless<F> {
_marker: PhantomData<F>,
}
impl<C: NativeClass, F: Method<C> + Copy + Default> Method<C> for Stateless<F> {
fn call(&self, this: TInstance<'_, C>, args: Varargs<'_>) -> Variant {
let f = F::default();
f.call(this, args)
}
}
#[derive(Clone, Copy, Default, Debug)]
pub struct StaticArgs<F> {
f: F,
}
impl<F> StaticArgs<F> {
#[inline]
pub fn new(f: F) -> Self {
StaticArgs { f }
}
}
pub trait StaticArgsMethod<C: NativeClass>: Send + Sync + 'static {
type Args: FromVarargs;
fn call(&self, this: TInstance<'_, C>, args: Self::Args) -> Variant;
#[inline]
fn site() -> Option<Site<'static>> {
None
}
}
impl<C: NativeClass, F: StaticArgsMethod<C>> Method<C> for StaticArgs<F> {
#[inline]
fn call(&self, this: TInstance<'_, C>, mut args: Varargs<'_>) -> Variant {
match args.read_many::<F::Args>() {
Ok(parsed) => {
if let Err(err) = args.done() {
err.with_site(F::site().unwrap_or_default()).log_error();
return Variant::nil();
}
F::call(&self.f, this, parsed)
}
Err(errors) => {
for err in errors {
err.with_site(F::site().unwrap_or_default()).log_error();
}
Variant::nil()
}
}
}
#[inline]
fn site() -> Option<Site<'static>> {
F::site()
}
}
pub struct Varargs<'a> {
idx: usize,
args: &'a [&'a Variant],
offset_index: usize,
}
impl<'a> Varargs<'a> {
#[inline]
pub fn len(&self) -> usize {
self.args.len() - self.idx
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn read<T: FromVariant>(&mut self) -> ArgBuilder<'_, 'a, T> {
ArgBuilder {
args: self,
name: None,
ty: None,
site: None,
_marker: PhantomData,
}
}
#[inline]
pub fn read_many<T: FromVarargs>(&mut self) -> Result<T, Vec<ArgumentError<'a>>> {
T::read(self)
}
#[inline]
pub fn as_slice(&self) -> &'a [&'a Variant] {
&self.args[self.idx..]
}
#[inline]
pub fn done(self) -> Result<(), ArgumentError<'a>> {
if self.is_empty() {
Ok(())
} else {
Err(ArgumentError {
site: None,
kind: ArgumentErrorKind::ExcessArguments {
rest: self.as_slice(),
},
})
}
}
#[doc(hidden)]
#[inline]
pub unsafe fn from_sys(num_args: libc::c_int, args: *mut *mut sys::godot_variant) -> Self {
let args = std::slice::from_raw_parts(args, num_args as usize);
let args = std::mem::transmute::<&[*mut sys::godot_variant], &[&Variant]>(args);
Self {
idx: 0,
args,
offset_index: 0,
}
}
#[inline]
pub fn check_length(&self, expected: impl Into<IndexBounds>) -> Result<(), VarargsError> {
let passed = self.args.len();
let expected = expected.into();
if expected.contains(passed) {
Ok(())
} else {
Err(VarargsError::InvalidLength {
length: passed,
expected,
})
}
}
#[inline]
pub fn get<T: FromVariant>(&self, index: usize) -> Result<T, VarargsError> {
match self.args.get(index) {
Some(v) => match T::from_variant(v) {
Ok(ok) => Ok(ok),
Err(error) => Err(VarargsError::InvalidArgumentType { index, error }),
},
None => {
let error = FromVariantError::Custom("Argument is not set".to_owned());
Err(VarargsError::InvalidArgumentType { index, error })
}
}
}
#[inline]
pub fn get_opt<T: FromVariant>(&self, index: usize) -> Result<Option<T>, VarargsError> {
match self.args.get(index) {
Some(v) => match T::from_variant(v) {
Ok(ok) => Ok(Some(ok)),
Err(error) => Err(VarargsError::InvalidArgumentType { index, error }),
},
None => Ok(None),
}
}
}
impl<'a> Iterator for Varargs<'a> {
type Item = &'a Variant;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let ret = self.args.get(self.idx);
ret.map(|&v| {
self.idx += 1;
v
})
}
}
macro_rules! replace_expr {
($_t:tt $sub:expr) => {
$sub
};
}
macro_rules! count_tts {
($($tts:tt)*) => {
0usize $(+ replace_expr!($tts 1usize))*
};
}
macro_rules! varargs_into_tuple {
($($params:ident),*) => {
impl<'a, $($params: FromVariant),*> std::convert::TryFrom<Varargs<'a>> for ($($params,)*) {
type Error = VarargsError;
#[inline]
fn try_from(args: Varargs<'a>) -> Result<Self, Self::Error> {
const EXPECTED: usize = count_tts!($($params)*);
args.check_length(EXPECTED)?;
let mut i: usize = 0;
#[allow(unused_variables, unused_mut)]
let mut inc = || {
let ret = i;
i += 1;
ret
};
Ok((
$(args.get::<$params>(inc())?,)*
))
}
}
};
}
varargs_into_tuple!();
varargs_into_tuple!(A);
varargs_into_tuple!(A, B);
varargs_into_tuple!(A, B, C);
varargs_into_tuple!(A, B, C, D);
varargs_into_tuple!(A, B, C, D, E);
varargs_into_tuple!(A, B, C, D, E, F);
varargs_into_tuple!(A, B, C, D, E, F, G);
varargs_into_tuple!(A, B, C, D, E, F, G, H);
varargs_into_tuple!(A, B, C, D, E, F, G, H, I);
varargs_into_tuple!(A, B, C, D, E, F, G, H, I, J);
varargs_into_tuple!(A, B, C, D, E, F, G, H, I, J, K);
varargs_into_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
#[derive(Debug)]
pub enum VarargsError {
InvalidArgumentType {
index: usize,
error: FromVariantError,
},
InvalidLength {
length: usize,
expected: IndexBounds,
},
}
impl std::error::Error for VarargsError {}
impl fmt::Display for VarargsError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
VarargsError::InvalidArgumentType { index, error } => {
write!(f, "type error for argument #{index}: {error}")?
}
VarargsError::InvalidLength { expected, length } => write!(
f,
"length mismatch: expected range {expected}, actual {length}"
)?,
}
Ok(())
}
}
pub struct IndexBounds {
pub start: Option<usize>,
pub end: Option<usize>,
}
impl IndexBounds {
#[inline]
pub fn contains(&self, value: usize) -> bool {
match (self.start, self.end) {
(Some(s), Some(e)) => value >= s && value <= e,
(Some(s), None) => value >= s,
(None, Some(e)) => value <= e,
(None, None) => false, }
}
}
impl From<usize> for IndexBounds {
#[inline]
fn from(exact_value: usize) -> Self {
Self {
start: Some(exact_value),
end: Some(exact_value),
}
}
}
impl From<ops::RangeInclusive<usize>> for IndexBounds {
#[inline]
fn from(range: ops::RangeInclusive<usize>) -> Self {
Self {
start: Some(*range.start()),
end: Some(*range.end()),
}
}
}
impl From<ops::RangeFrom<usize>> for IndexBounds {
#[inline]
fn from(range: ops::RangeFrom<usize>) -> Self {
Self {
start: Some(range.start),
end: None,
}
}
}
impl From<ops::RangeToInclusive<usize>> for IndexBounds {
#[inline]
fn from(range: ops::RangeToInclusive<usize>) -> Self {
Self {
start: None,
end: Some(range.end),
}
}
}
impl fmt::Debug for IndexBounds {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "IndexBounds({self})")
}
}
impl fmt::Display for IndexBounds {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(start) = self.start {
write!(f, "{start}")?
}
write!(f, "..=")?;
if let Some(end) = self.end {
write!(f, "{end}")?
}
Ok(())
}
}
pub trait FromVarargs: Sized {
fn read<'a>(args: &mut Varargs<'a>) -> Result<Self, Vec<ArgumentError<'a>>>;
}
pub struct ArgBuilder<'r, 'a, T> {
args: &'r mut Varargs<'a>,
name: Option<Cow<'a, str>>,
ty: Option<Cow<'a, str>>,
site: Option<Site<'a>>,
_marker: PhantomData<T>,
}
impl<'r, 'a, T> ArgBuilder<'r, 'a, T> {
#[inline]
pub fn with_name<S: Into<Cow<'a, str>>>(mut self, name: S) -> Self {
self.name = Some(name.into());
self
}
#[inline]
pub fn with_type_name<S: Into<Cow<'a, str>>>(mut self, ty: S) -> Self {
self.ty = Some(ty.into());
self
}
#[inline]
pub fn with_site(mut self, site: Site<'a>) -> Self {
self.site = Some(site);
self
}
}
impl<'r, 'a, T: FromVariant> ArgBuilder<'r, 'a, T> {
#[inline]
pub fn get(mut self) -> Result<T, ArgumentError<'a>> {
self.get_optional_internal().and_then(|arg| {
let actual_index = self.args.idx + self.args.offset_index;
arg.ok_or(ArgumentError {
site: self.site,
kind: ArgumentErrorKind::Missing {
idx: actual_index,
name: self.name,
},
})
})
}
#[inline]
pub fn get_optional(mut self) -> Result<Option<T>, ArgumentError<'a>> {
self.get_optional_internal()
}
fn get_optional_internal(&mut self) -> Result<Option<T>, ArgumentError<'a>> {
let Self {
site,
args,
name,
ty,
..
} = self;
let actual_index = args.idx + args.offset_index;
if let Some(arg) = args.next() {
T::from_variant(arg).map(Some).map_err(|err| ArgumentError {
site: *site,
kind: ArgumentErrorKind::CannotConvert {
idx: actual_index,
name: name.take(),
value: arg,
ty: ty
.take()
.unwrap_or_else(|| Cow::Borrowed(std::any::type_name::<T>())),
err,
},
})
} else {
Ok(None)
}
}
}
#[derive(Debug)]
pub struct ArgumentError<'a> {
site: Option<Site<'a>>,
kind: ArgumentErrorKind<'a>,
}
impl<'a> std::error::Error for ArgumentError<'a> {}
impl<'a> fmt::Display for ArgumentError<'a> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(site) = &self.site {
write!(f, "at {site}: ")?;
}
write!(f, "{}", self.kind)
}
}
impl<'a> ArgumentError<'a> {
#[inline]
pub fn with_site(mut self, site: Site<'a>) -> Self {
self.site = Some(site);
self
}
#[inline]
pub fn log_warn(&self) {
crate::log::warn(self.site.unwrap_or_default(), &self.kind);
}
#[inline]
pub fn log_error(&self) {
crate::log::error(self.site.unwrap_or_default(), &self.kind);
}
}
#[derive(Debug)]
enum ArgumentErrorKind<'a> {
Missing {
idx: usize,
name: Option<Cow<'a, str>>,
},
CannotConvert {
idx: usize,
name: Option<Cow<'a, str>>,
ty: Cow<'a, str>,
value: &'a Variant,
err: FromVariantError,
},
ExcessArguments {
rest: &'a [&'a Variant],
},
}
impl<'a> std::error::Error for ArgumentErrorKind<'a> {}
impl<'a> fmt::Display for ArgumentErrorKind<'a> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ArgumentErrorKind as E;
match self {
E::Missing {
idx,
name: Some(name),
} => {
write!(f, "missing non-optional parameter `{name}` (#{idx})")
}
E::Missing { idx, name: None } => {
write!(f, "missing non-optional parameter #{idx}")
}
E::CannotConvert {
idx,
name: Some(name),
value,
ty,
err,
} => {
write!(f,
"cannot convert argument `{name}` (#{idx}, {value:?}) to {ty}: {err} (non-primitive types may impose structural checks)"
)
}
E::CannotConvert {
idx,
name: None,
value,
ty,
err,
} => {
write!(f,
"cannot convert argument #{idx} ({value:?}) to {ty}: {err} (non-primitive types may impose structural checks)"
)
}
E::ExcessArguments { rest } => {
if rest.len() > 1 {
write!(
f,
"{} excessive arguments are given: {:?}",
rest.len(),
rest
)
} else {
write!(f, "an excessive argument is given: {:?}", rest[0])
}
}
}
}
}
unsafe extern "C" fn method_wrapper<C: NativeClass, F: Method<C>>(
this: *mut sys::godot_object,
method_data: *mut libc::c_void,
user_data: *mut libc::c_void,
num_args: libc::c_int,
args: *mut *mut sys::godot_variant,
) -> sys::godot_variant {
if user_data.is_null() {
crate::log::error(
F::site().unwrap_or_default(),
format_args!(
"gdnative-core: user data pointer for {} is null (did the constructor fail?)",
class_registry::class_name_or_default::<C>(),
),
);
return Variant::nil().leak();
}
let this = match std::ptr::NonNull::new(this) {
Some(this) => this,
None => {
crate::log::error(
F::site().unwrap_or_default(),
format_args!(
"gdnative-core: base object pointer for {} is null (probably a bug in Godot)",
class_registry::class_name_or_default::<C>(),
),
);
return Variant::nil().leak();
}
};
let result = std::panic::catch_unwind(move || {
let method = &*(method_data as *const F);
let this: Ref<C::Base, Shared> = Ref::from_sys(this);
let this: TRef<'_, C::Base, _> = this.assume_safe_unchecked();
let this: TInstance<'_, C, _> = TInstance::from_raw_unchecked(this, user_data);
let args = Varargs::from_sys(num_args, args);
F::call(method, this, args)
});
result
.unwrap_or_else(|e| {
crate::log::error(
F::site().unwrap_or_default(),
"gdnative-core: method panicked (check stderr for output)",
);
crate::private::print_panic_error(e);
Variant::nil()
})
.leak()
}
unsafe extern "C" fn free_func<F>(method_data: *mut libc::c_void) {
drop(Box::from_raw(method_data as *mut F))
}