gdnative_core/export/
class_builder.rs

1use std::any::TypeId;
2use std::cell::RefCell;
3use std::collections::HashSet;
4use std::ffi::CString;
5use std::marker::PhantomData;
6use std::ptr;
7
8use crate::core_types::{GodotString, VariantType};
9use crate::export::*;
10use crate::object::NewRef;
11use crate::private::get_api;
12
13// TODO(#996): unify string parameters across all buiders
14// Potential candidates:
15// * &str
16// * impl Into<GodotString>
17// * impl Into<Cow<'a, str>>
18
19/// Allows registration of exported properties, methods and signals.
20///
21/// See member functions of this class for usage examples.
22#[derive(Debug)]
23pub struct ClassBuilder<C> {
24    pub(super) init_handle: *mut libc::c_void,
25    pub(super) class_name: CString,
26    mixins: RefCell<HashSet<TypeId, ahash::RandomState>>,
27    _marker: PhantomData<C>,
28}
29
30impl<C: NativeClass> ClassBuilder<C> {
31    pub(crate) fn new(init_handle: *mut libc::c_void, class_name: CString) -> Self {
32        Self {
33            init_handle,
34            class_name,
35            mixins: RefCell::default(),
36            _marker: PhantomData,
37        }
38    }
39
40    /// Returns a `MethodBuilder` which can be used to add a method to the class being
41    /// registered.
42    ///
43    /// # Examples
44    ///
45    /// Basic usage:
46    /// ```
47    /// use gdnative::prelude::*;
48    /// use gdnative::export::{RpcMode, Varargs};
49    ///
50    /// #[derive(NativeClass)]
51    /// #[register_with(Self::my_register)]
52    /// #[no_constructor]
53    /// struct MyType {}
54    ///
55    /// // Note: no #[methods] required
56    /// impl MyType {
57    ///     fn my_method(&self) -> i64 { 42 }
58    ///
59    ///     fn my_register(builder: &ClassBuilder<MyType>) {
60    ///         builder
61    ///             .method("my_method", MyMethod)
62    ///             .with_rpc_mode(RpcMode::RemoteSync)
63    ///             .done();
64    ///     }
65    /// }
66    ///
67    /// // Now, wrap the method (this can do anything and does not need to actually call a method)
68    /// struct MyMethod;
69    /// impl Method<MyType> for MyMethod {
70    ///     fn call(&self, this: TInstance<'_, MyType>, _args: Varargs<'_>) -> Variant {
71    ///         this.map(|obj: &MyType, _| {
72    ///             let result = obj.my_method();
73    ///             Variant::new(result)
74    ///         }).expect("method call succeeds")
75    ///     }
76    /// }
77    /// ```
78    ///
79    #[inline]
80    pub fn method<'a, F: Method<C>>(&'a self, name: &'a str, method: F) -> MethodBuilder<'a, C, F> {
81        MethodBuilder::new(self, name, method)
82    }
83
84    /// Returns a `PropertyBuilder` which can be used to add a property to the class being
85    /// registered.
86    ///
87    /// # Examples
88    ///
89    /// Basic usage:
90    ///
91    /// ```
92    /// use gdnative::prelude::*;
93    ///
94    /// #[derive(NativeClass)]
95    /// #[inherit(Node)]
96    /// #[register_with(Self::my_register)]
97    /// #[no_constructor]
98    /// struct MyType {
99    ///     foo: i32,
100    /// }
101    ///
102    /// // Note: no #[methods] required
103    /// impl MyType {
104    ///     pub fn get_foo(&self, _owner: TRef<Node>) -> i32 { self.foo }
105    ///     pub fn set_foo(&mut self, _owner: TRef<Node>, val: i32) { self.foo = val; }
106    ///
107    ///     fn my_register(builder: &ClassBuilder<MyType>) {
108    ///         builder
109    ///             .property("foo")
110    ///             .with_default(5)
111    ///             .with_hint((-10..=30).into())
112    ///             .with_getter(MyType::get_foo)
113    ///             .with_setter(MyType::set_foo)
114    ///             .done();
115    ///     }
116    /// }
117    /// ```
118    #[inline]
119    pub fn property<'a, T>(&'a self, name: &'a str) -> PropertyBuilder<'a, C, T>
120    where
121        T: Export,
122    {
123        PropertyBuilder::new(self, name)
124    }
125
126    /// Returns a `SignalBuilder` which can be used to add a signal to the class being
127    /// registered.
128    ///
129    /// # Examples
130    ///
131    /// Basic usage:
132    ///
133    /// ```
134    /// use gdnative::prelude::*;
135    ///
136    /// #[derive(NativeClass)]
137    /// #[inherit(Node)]
138    /// #[register_with(Self::my_register)]
139    /// #[no_constructor]
140    /// struct MyType {}
141    ///
142    /// // Note: no #[methods] required
143    /// impl MyType {
144    ///     fn my_register(builder: &ClassBuilder<MyType>) {
145    ///         // Add signal without parameters
146    ///         builder
147    ///             .signal("jumped")
148    ///             .done();
149    ///
150    ///         // Add another signal with 1 parameter (untyped)
151    ///         builder
152    ///             .signal("fired")
153    ///             .with_param_untyped("weapon_type")
154    ///             .done();
155    ///
156    ///         // Add third signal with int + String parameters, the latter with a default value "Kerosene"
157    ///         builder
158    ///             .signal("used_jetpack")
159    ///             .with_param("fuel_spent", VariantType::I64)
160    ///             .with_param_default("fuel_type", Variant::new("Kerosene"))
161    ///             .done();
162    ///     }
163    /// }
164    /// ```
165    #[inline]
166    pub fn signal(&self, name: &str) -> SignalBuilder<C> {
167        SignalBuilder::new(self, GodotString::from(name))
168    }
169
170    #[inline]
171    pub(crate) fn add_signal(&self, signal: Signal) {
172        unsafe {
173            let args_and_hints = signal
174                .args
175                .iter()
176                .map(|arg| {
177                    let hint_string = arg.export_info.hint_string.new_ref();
178                    (arg, hint_string)
179                })
180                .collect::<Vec<_>>();
181
182            let mut sys_args = args_and_hints
183                .iter()
184                .map(|(param, hint_string)| sys::godot_signal_argument {
185                    name: param.name.to_sys(),
186                    type_: Self::get_param_type(param) as i32,
187                    hint: param.export_info.hint_kind,
188                    hint_string: hint_string.to_sys(),
189                    usage: param.usage.to_sys(),
190                    default_value: param.default.to_sys(),
191                })
192                .collect::<Vec<_>>();
193
194            (get_api().godot_nativescript_register_signal)(
195                self.init_handle,
196                self.class_name.as_ptr(),
197                &sys::godot_signal {
198                    name: signal.name.to_sys(),
199                    num_args: sys_args.len() as i32,
200                    args: sys_args.as_mut_ptr(),
201                    num_default_args: 0,
202                    default_args: ptr::null_mut(),
203                },
204            );
205        }
206    }
207
208    /// Returns the declared parameter type, or the default value's type, or Nil (in that order)
209    fn get_param_type(arg: &SignalParam) -> VariantType {
210        let export_type = arg.export_info.variant_type;
211        if export_type != VariantType::Nil {
212            export_type
213        } else {
214            arg.default.get_type()
215        }
216    }
217
218    pub(crate) fn add_method(&self, method: ScriptMethod) {
219        let method_name = CString::new(method.name).unwrap();
220
221        let attr = sys::godot_method_attributes {
222            rpc_type: method.attributes.rpc_mode.sys(),
223        };
224
225        let method_desc = sys::godot_instance_method {
226            method: method.method_ptr,
227            method_data: method.method_data,
228            free_func: method.free_func,
229        };
230
231        unsafe {
232            (get_api().godot_nativescript_register_method)(
233                self.init_handle,
234                self.class_name.as_ptr() as *const _,
235                method_name.as_ptr() as *const _,
236                attr,
237                method_desc,
238            );
239        }
240    }
241
242    /// Add a mixin to the class being registered.
243    ///
244    /// # Examples
245    ///
246    /// ```
247    /// use gdnative::prelude::*;
248    ///
249    /// #[derive(NativeClass)]
250    /// #[inherit(Node)]
251    /// #[register_with(my_register)]
252    /// #[no_constructor]
253    /// struct MyType {}
254    ///
255    /// // This creates a opaque type `MyMixin` in the current scope that implements
256    /// // the `Mixin` trait. Mixin types have no public interface or stable layout.
257    /// #[methods(mixin = "MyMixin")]
258    /// impl MyType {
259    ///     #[method]
260    ///     fn my_method(&self) -> i64 { 42 }
261    /// }
262    ///
263    /// fn my_register(builder: &ClassBuilder<MyType>) {
264    ///     builder.mixin::<MyMixin>();
265    /// }
266    /// ```
267    #[inline]
268    pub fn mixin<M: Mixin<C>>(&self) {
269        if self.mixins.borrow_mut().insert(TypeId::of::<M>()) {
270            M::register(self);
271        }
272    }
273}
274
275/// Trait for mixins, manually registered `#[methods]` blocks that may be applied to multiple types.
276///
277/// This trait is implemented on generated types by the `#[methods]` proc-macro only, and has no public interface.
278/// Use [`ClassBuilder::mixin`] to register mixins to [`NativeClass`] types.    
279pub trait Mixin<C>: crate::private::mixin::Sealed + 'static
280where
281    C: NativeClass,
282{
283    #[doc(hidden)]
284    fn register(builder: &ClassBuilder<C>);
285}