1use std::borrow::Cow;
4use std::marker::PhantomData;
5use std::{fmt, ops};
6
7use crate::core_types::{FromVariant, FromVariantError, Variant};
8use crate::export::class::NativeClass;
9use crate::export::{class_registry, ClassBuilder};
10use crate::log::Site;
11use crate::object::ownership::Shared;
12use crate::object::{Ref, TInstance, TRef};
13
14#[must_use = "MethodBuilder left unbuilt -- did you forget to call done() or done_stateless()?"]
16pub struct MethodBuilder<'a, C, F> {
17 class_builder: &'a ClassBuilder<C>,
18 name: &'a str,
19 method: F,
20
21 rpc_mode: RpcMode,
22}
23
24impl<'a, C, F> MethodBuilder<'a, C, F>
25where
26 C: NativeClass,
27 F: Method<C>,
28{
29 pub(super) fn new(class_builder: &'a ClassBuilder<C>, name: &'a str, method: F) -> Self {
30 MethodBuilder {
31 class_builder,
32 name,
33 method,
34 rpc_mode: RpcMode::Disabled,
35 }
36 }
37
38 #[inline]
40 pub fn with_rpc_mode(mut self, rpc_mode: RpcMode) -> Self {
41 self.rpc_mode = rpc_mode;
42 self
43 }
44
45 #[inline]
47 pub fn done(self) {
48 let method_data = Box::into_raw(Box::new(self.method));
49
50 let script_method = ScriptMethod {
51 name: self.name,
52 method_ptr: Some(method_wrapper::<C, F>),
53 attributes: ScriptMethodAttributes {
54 rpc_mode: self.rpc_mode,
55 },
56 method_data: method_data as *mut libc::c_void,
57 free_func: Some(free_func::<F>),
58 };
59
60 self.class_builder.add_method(script_method);
61 }
62}
63
64impl<'a, C, F> MethodBuilder<'a, C, F>
65where
66 C: NativeClass,
67 F: Method<C> + Copy + Default,
68{
69 #[inline]
73 pub fn done_stateless(self) {
74 let script_method = ScriptMethod {
75 name: self.name,
76 method_ptr: Some(method_wrapper::<C, Stateless<F>>),
77 attributes: ScriptMethodAttributes {
78 rpc_mode: self.rpc_mode,
79 },
80
81 method_data: 1 as *mut libc::c_void,
84 free_func: None,
85 };
86
87 self.class_builder.add_method(script_method);
88 }
89}
90
91type ScriptMethodFn = unsafe extern "C" fn(
92 *mut sys::godot_object,
93 *mut libc::c_void,
94 *mut libc::c_void,
95 libc::c_int,
96 *mut *mut sys::godot_variant,
97) -> sys::godot_variant;
98
99#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
100pub enum RpcMode {
101 Disabled,
102 Remote,
103 RemoteSync,
104 Master,
105 Puppet,
106 MasterSync,
107 PuppetSync,
108}
109
110impl Default for RpcMode {
111 #[inline]
112 fn default() -> Self {
113 RpcMode::Disabled
114 }
115}
116
117impl RpcMode {
118 pub(crate) fn sys(self) -> sys::godot_method_rpc_mode {
119 match self {
120 RpcMode::Master => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_MASTER,
121 RpcMode::Remote => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_REMOTE,
122 RpcMode::Puppet => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_PUPPET,
123 RpcMode::RemoteSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_REMOTESYNC,
124 RpcMode::Disabled => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_DISABLED,
125 RpcMode::MasterSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_MASTERSYNC,
126 RpcMode::PuppetSync => sys::godot_method_rpc_mode_GODOT_METHOD_RPC_MODE_PUPPETSYNC,
127 }
128 }
129}
130
131pub(crate) struct ScriptMethodAttributes {
132 pub rpc_mode: RpcMode,
133}
134
135pub(crate) struct ScriptMethod<'l> {
136 pub name: &'l str,
137 pub method_ptr: Option<ScriptMethodFn>,
138 pub attributes: ScriptMethodAttributes,
139
140 pub method_data: *mut libc::c_void,
141 pub free_func: Option<unsafe extern "C" fn(*mut libc::c_void) -> ()>,
142}
143
144pub trait Method<C: NativeClass>: Send + Sync + 'static {
146 fn call(&self, this: TInstance<'_, C>, args: Varargs<'_>) -> Variant;
148
149 #[inline]
153 fn site() -> Option<Site<'static>> {
154 None
155 }
156}
157
158struct Stateless<F> {
160 _marker: PhantomData<F>,
161}
162
163impl<C: NativeClass, F: Method<C> + Copy + Default> Method<C> for Stateless<F> {
164 fn call(&self, this: TInstance<'_, C>, args: Varargs<'_>) -> Variant {
165 let f = F::default();
166 f.call(this, args)
167 }
168}
169
170#[derive(Clone, Copy, Default, Debug)]
173pub struct StaticArgs<F> {
174 f: F,
175}
176
177impl<F> StaticArgs<F> {
178 #[inline]
180 pub fn new(f: F) -> Self {
181 StaticArgs { f }
182 }
183}
184
185pub trait StaticArgsMethod<C: NativeClass>: Send + Sync + 'static {
188 type Args: FromVarargs;
189 fn call(&self, this: TInstance<'_, C>, args: Self::Args) -> Variant;
190
191 #[inline]
195 fn site() -> Option<Site<'static>> {
196 None
197 }
198}
199
200impl<C: NativeClass, F: StaticArgsMethod<C>> Method<C> for StaticArgs<F> {
201 #[inline]
202 fn call(&self, this: TInstance<'_, C>, mut args: Varargs<'_>) -> Variant {
203 match args.read_many::<F::Args>() {
204 Ok(parsed) => {
205 if let Err(err) = args.done() {
206 err.with_site(F::site().unwrap_or_default()).log_error();
207 return Variant::nil();
208 }
209 F::call(&self.f, this, parsed)
210 }
211 Err(errors) => {
212 for err in errors {
213 err.with_site(F::site().unwrap_or_default()).log_error();
214 }
215 Variant::nil()
216 }
217 }
218 }
219
220 #[inline]
221 fn site() -> Option<Site<'static>> {
222 F::site()
223 }
224}
225
226pub struct Varargs<'a> {
256 idx: usize,
257 args: &'a [&'a Variant],
258 offset_index: usize,
259}
260
261impl<'a> Varargs<'a> {
262 #[inline]
264 pub fn len(&self) -> usize {
265 self.args.len() - self.idx
266 }
267
268 #[inline]
269 pub fn is_empty(&self) -> bool {
270 self.len() == 0
271 }
272
273 #[inline]
276 pub fn read<T: FromVariant>(&mut self) -> ArgBuilder<'_, 'a, T> {
277 ArgBuilder {
278 args: self,
279 name: None,
280 ty: None,
281 site: None,
282 _marker: PhantomData,
283 }
284 }
285
286 #[inline]
289 pub fn read_many<T: FromVarargs>(&mut self) -> Result<T, Vec<ArgumentError<'a>>> {
290 T::read(self)
291 }
292
293 #[inline]
295 pub fn as_slice(&self) -> &'a [&'a Variant] {
296 &self.args[self.idx..]
297 }
298
299 #[inline]
305 pub fn done(self) -> Result<(), ArgumentError<'a>> {
306 if self.is_empty() {
307 Ok(())
308 } else {
309 Err(ArgumentError {
310 site: None,
311 kind: ArgumentErrorKind::ExcessArguments {
312 rest: self.as_slice(),
313 },
314 })
315 }
316 }
317
318 #[doc(hidden)]
324 #[inline]
325 pub unsafe fn from_sys(num_args: libc::c_int, args: *mut *mut sys::godot_variant) -> Self {
326 let args = std::slice::from_raw_parts(args, num_args as usize);
327 let args = std::mem::transmute::<&[*mut sys::godot_variant], &[&Variant]>(args);
328 Self {
329 idx: 0,
330 args,
331 offset_index: 0,
332 }
333 }
334
335 #[inline]
342 pub fn check_length(&self, expected: impl Into<IndexBounds>) -> Result<(), VarargsError> {
343 let passed = self.args.len();
344 let expected = expected.into();
345 if expected.contains(passed) {
346 Ok(())
347 } else {
348 Err(VarargsError::InvalidLength {
350 length: passed,
351 expected,
352 })
353 }
354 }
355
356 #[inline]
371 pub fn get<T: FromVariant>(&self, index: usize) -> Result<T, VarargsError> {
372 match self.args.get(index) {
375 Some(v) => match T::from_variant(v) {
376 Ok(ok) => Ok(ok),
377 Err(error) => Err(VarargsError::InvalidArgumentType { index, error }),
378 },
379 None => {
380 let error = FromVariantError::Custom("Argument is not set".to_owned());
381 Err(VarargsError::InvalidArgumentType { index, error })
382 }
383 }
384 }
385
386 #[inline]
402 pub fn get_opt<T: FromVariant>(&self, index: usize) -> Result<Option<T>, VarargsError> {
403 match self.args.get(index) {
406 Some(v) => match T::from_variant(v) {
407 Ok(ok) => Ok(Some(ok)),
408 Err(error) => Err(VarargsError::InvalidArgumentType { index, error }),
409 },
410 None => Ok(None),
411 }
412 }
413}
414
415impl<'a> Iterator for Varargs<'a> {
416 type Item = &'a Variant;
417 #[inline]
418 fn next(&mut self) -> Option<Self::Item> {
419 let ret = self.args.get(self.idx);
420 ret.map(|&v| {
421 self.idx += 1;
422 v
423 })
424 }
425}
426
427macro_rules! replace_expr {
429 ($_t:tt $sub:expr) => {
430 $sub
431 };
432}
433
434macro_rules! count_tts {
436 ($($tts:tt)*) => {
437 0usize $(+ replace_expr!($tts 1usize))*
438 };
439}
440
441macro_rules! varargs_into_tuple {
443 ($($params:ident),*) => {
444 impl<'a, $($params: FromVariant),*> std::convert::TryFrom<Varargs<'a>> for ($($params,)*) {
445 type Error = VarargsError;
446
447 #[inline]
448 fn try_from(args: Varargs<'a>) -> Result<Self, Self::Error> {
449 const EXPECTED: usize = count_tts!($($params)*);
450 args.check_length(EXPECTED)?;
451 let mut i: usize = 0;
452 #[allow(unused_variables, unused_mut)]
453 let mut inc = || {
454 let ret = i;
455 i += 1;
456 ret
457 };
458 Ok((
459 $(args.get::<$params>(inc())?,)*
460 ))
461 }
462 }
463 };
464}
465
466varargs_into_tuple!();
468varargs_into_tuple!(A);
469varargs_into_tuple!(A, B);
470varargs_into_tuple!(A, B, C);
471varargs_into_tuple!(A, B, C, D);
472varargs_into_tuple!(A, B, C, D, E);
473varargs_into_tuple!(A, B, C, D, E, F);
474varargs_into_tuple!(A, B, C, D, E, F, G);
475varargs_into_tuple!(A, B, C, D, E, F, G, H);
476varargs_into_tuple!(A, B, C, D, E, F, G, H, I);
477varargs_into_tuple!(A, B, C, D, E, F, G, H, I, J);
478varargs_into_tuple!(A, B, C, D, E, F, G, H, I, J, K);
479varargs_into_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
480
481#[derive(Debug)]
486pub enum VarargsError {
487 InvalidArgumentType {
489 index: usize,
490 error: FromVariantError,
491 },
492 InvalidLength {
494 length: usize,
496 expected: IndexBounds,
497 },
498}
499
500impl std::error::Error for VarargsError {}
501
502impl fmt::Display for VarargsError {
503 #[inline]
504 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505 match self {
506 VarargsError::InvalidArgumentType { index, error } => {
507 write!(f, "type error for argument #{index}: {error}")?
508 }
509 VarargsError::InvalidLength { expected, length } => write!(
510 f,
511 "length mismatch: expected range {expected}, actual {length}"
512 )?,
513 }
514
515 Ok(())
516 }
517}
518
519pub struct IndexBounds {
523 pub start: Option<usize>,
525
526 pub end: Option<usize>,
528}
529
530impl IndexBounds {
531 #[inline]
532 pub fn contains(&self, value: usize) -> bool {
533 match (self.start, self.end) {
534 (Some(s), Some(e)) => value >= s && value <= e,
535 (Some(s), None) => value >= s,
536 (None, Some(e)) => value <= e,
537 (None, None) => false, }
539 }
540}
541
542impl From<usize> for IndexBounds {
544 #[inline]
545 fn from(exact_value: usize) -> Self {
546 Self {
547 start: Some(exact_value),
548 end: Some(exact_value),
549 }
550 }
551}
552
553impl From<ops::RangeInclusive<usize>> for IndexBounds {
555 #[inline]
556 fn from(range: ops::RangeInclusive<usize>) -> Self {
557 Self {
558 start: Some(*range.start()),
559 end: Some(*range.end()),
560 }
561 }
562}
563
564impl From<ops::RangeFrom<usize>> for IndexBounds {
566 #[inline]
567 fn from(range: ops::RangeFrom<usize>) -> Self {
568 Self {
569 start: Some(range.start),
570 end: None,
571 }
572 }
573}
574
575impl From<ops::RangeToInclusive<usize>> for IndexBounds {
577 #[inline]
578 fn from(range: ops::RangeToInclusive<usize>) -> Self {
579 Self {
580 start: None,
581 end: Some(range.end),
582 }
583 }
584}
585
586impl fmt::Debug for IndexBounds {
587 #[inline]
588 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
589 write!(f, "IndexBounds({self})")
590 }
591}
592
593impl fmt::Display for IndexBounds {
594 #[inline]
595 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
596 if let Some(start) = self.start {
597 write!(f, "{start}")?
598 }
599
600 write!(f, "..=")?;
601
602 if let Some(end) = self.end {
603 write!(f, "{end}")?
604 }
605
606 Ok(())
607 }
608}
609
610pub trait FromVarargs: Sized {
624 fn read<'a>(args: &mut Varargs<'a>) -> Result<Self, Vec<ArgumentError<'a>>>;
625}
626
627pub struct ArgBuilder<'r, 'a, T> {
629 args: &'r mut Varargs<'a>,
630 name: Option<Cow<'a, str>>,
631 ty: Option<Cow<'a, str>>,
632 site: Option<Site<'a>>,
633 _marker: PhantomData<T>,
634}
635
636impl<'r, 'a, T> ArgBuilder<'r, 'a, T> {
637 #[inline]
641 pub fn with_name<S: Into<Cow<'a, str>>>(mut self, name: S) -> Self {
642 self.name = Some(name.into());
643 self
644 }
645
646 #[inline]
651 pub fn with_type_name<S: Into<Cow<'a, str>>>(mut self, ty: S) -> Self {
652 self.ty = Some(ty.into());
653 self
654 }
655
656 #[inline]
659 pub fn with_site(mut self, site: Site<'a>) -> Self {
660 self.site = Some(site);
661 self
662 }
663}
664
665impl<'r, 'a, T: FromVariant> ArgBuilder<'r, 'a, T> {
666 #[inline]
672 pub fn get(mut self) -> Result<T, ArgumentError<'a>> {
673 self.get_optional_internal().and_then(|arg| {
674 let actual_index = self.args.idx + self.args.offset_index;
675 arg.ok_or(ArgumentError {
676 site: self.site,
677 kind: ArgumentErrorKind::Missing {
678 idx: actual_index,
679 name: self.name,
680 },
681 })
682 })
683 }
684
685 #[inline]
691 pub fn get_optional(mut self) -> Result<Option<T>, ArgumentError<'a>> {
692 self.get_optional_internal()
693 }
694
695 fn get_optional_internal(&mut self) -> Result<Option<T>, ArgumentError<'a>> {
696 let Self {
697 site,
698 args,
699 name,
700 ty,
701 ..
702 } = self;
703 let actual_index = args.idx + args.offset_index;
704
705 if let Some(arg) = args.next() {
706 T::from_variant(arg).map(Some).map_err(|err| ArgumentError {
707 site: *site,
708 kind: ArgumentErrorKind::CannotConvert {
709 idx: actual_index,
710 name: name.take(),
711 value: arg,
712 ty: ty
713 .take()
714 .unwrap_or_else(|| Cow::Borrowed(std::any::type_name::<T>())),
715 err,
716 },
717 })
718 } else {
719 Ok(None)
720 }
721 }
722}
723
724#[derive(Debug)]
726pub struct ArgumentError<'a> {
727 site: Option<Site<'a>>,
728 kind: ArgumentErrorKind<'a>,
729}
730
731impl<'a> std::error::Error for ArgumentError<'a> {}
732impl<'a> fmt::Display for ArgumentError<'a> {
733 #[inline]
734 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
735 if let Some(site) = &self.site {
736 write!(f, "at {site}: ")?;
737 }
738 write!(f, "{}", self.kind)
739 }
740}
741
742impl<'a> ArgumentError<'a> {
743 #[inline]
746 pub fn with_site(mut self, site: Site<'a>) -> Self {
747 self.site = Some(site);
748 self
749 }
750
751 #[inline]
757 pub fn log_warn(&self) {
758 crate::log::warn(self.site.unwrap_or_default(), &self.kind);
759 }
760
761 #[inline]
767 pub fn log_error(&self) {
768 crate::log::error(self.site.unwrap_or_default(), &self.kind);
769 }
770}
771
772#[derive(Debug)]
774enum ArgumentErrorKind<'a> {
775 Missing {
776 idx: usize,
777 name: Option<Cow<'a, str>>,
778 },
779 CannotConvert {
780 idx: usize,
781 name: Option<Cow<'a, str>>,
782 ty: Cow<'a, str>,
783 value: &'a Variant,
784 err: FromVariantError,
785 },
786 ExcessArguments {
787 rest: &'a [&'a Variant],
788 },
789}
790
791impl<'a> std::error::Error for ArgumentErrorKind<'a> {}
792
793impl<'a> fmt::Display for ArgumentErrorKind<'a> {
794 #[inline]
795 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
796 use ArgumentErrorKind as E;
797
798 match self {
799 E::Missing {
800 idx,
801 name: Some(name),
802 } => {
803 write!(f, "missing non-optional parameter `{name}` (#{idx})")
804 }
805 E::Missing { idx, name: None } => {
806 write!(f, "missing non-optional parameter #{idx}")
807 }
808 E::CannotConvert {
809 idx,
810 name: Some(name),
811 value,
812 ty,
813 err,
814 } => {
815 write!(f,
816 "cannot convert argument `{name}` (#{idx}, {value:?}) to {ty}: {err} (non-primitive types may impose structural checks)"
817 )
818 }
819 E::CannotConvert {
820 idx,
821 name: None,
822 value,
823 ty,
824 err,
825 } => {
826 write!(f,
827 "cannot convert argument #{idx} ({value:?}) to {ty}: {err} (non-primitive types may impose structural checks)"
828 )
829 }
830 E::ExcessArguments { rest } => {
831 if rest.len() > 1 {
832 write!(
833 f,
834 "{} excessive arguments are given: {:?}",
835 rest.len(),
836 rest
837 )
838 } else {
839 write!(f, "an excessive argument is given: {:?}", rest[0])
840 }
841 }
842 }
843 }
844}
845
846unsafe extern "C" fn method_wrapper<C: NativeClass, F: Method<C>>(
847 this: *mut sys::godot_object,
848 method_data: *mut libc::c_void,
849 user_data: *mut libc::c_void,
850 num_args: libc::c_int,
851 args: *mut *mut sys::godot_variant,
852) -> sys::godot_variant {
853 if user_data.is_null() {
854 crate::log::error(
855 F::site().unwrap_or_default(),
856 format_args!(
857 "gdnative-core: user data pointer for {} is null (did the constructor fail?)",
858 class_registry::class_name_or_default::<C>(),
859 ),
860 );
861 return Variant::nil().leak();
862 }
863
864 let this = match std::ptr::NonNull::new(this) {
865 Some(this) => this,
866 None => {
867 crate::log::error(
868 F::site().unwrap_or_default(),
869 format_args!(
870 "gdnative-core: base object pointer for {} is null (probably a bug in Godot)",
871 class_registry::class_name_or_default::<C>(),
872 ),
873 );
874 return Variant::nil().leak();
875 }
876 };
877
878 let result = std::panic::catch_unwind(move || {
879 let method = &*(method_data as *const F);
880
881 let this: Ref<C::Base, Shared> = Ref::from_sys(this);
882 let this: TRef<'_, C::Base, _> = this.assume_safe_unchecked();
883 let this: TInstance<'_, C, _> = TInstance::from_raw_unchecked(this, user_data);
884
885 let args = Varargs::from_sys(num_args, args);
886
887 F::call(method, this, args)
888 });
889
890 result
891 .unwrap_or_else(|e| {
892 crate::log::error(
893 F::site().unwrap_or_default(),
894 "gdnative-core: method panicked (check stderr for output)",
895 );
896 crate::private::print_panic_error(e);
897 Variant::nil()
898 })
899 .leak()
900}
901
902unsafe extern "C" fn free_func<F>(method_data: *mut libc::c_void) {
903 drop(Box::from_raw(method_data as *mut F))
904}