diff --git a/gdext-builtin/src/arrays.rs b/gdext-builtin/src/arrays.rs new file mode 100644 index 000000000..c9581b58a --- /dev/null +++ b/gdext-builtin/src/arrays.rs @@ -0,0 +1,139 @@ +use crate::Variant; +use gdext_sys as sys; +use std::marker::PhantomData; +use std::ops::{Index, IndexMut}; +use sys::{ffi_methods, interface_fn, types::*, GodotFfi}; + +impl_builtin_stub!(Array, OpaqueArray); +impl_builtin_stub!(ByteArray, OpaquePackedByteArray); +impl_builtin_stub!(ColorArray, OpaquePackedColorArray); +impl_builtin_stub!(Float32Array, OpaquePackedFloat32Array); +impl_builtin_stub!(Float64Array, OpaquePackedFloat64Array); +impl_builtin_stub!(Int32Array, OpaquePackedInt32Array); +impl_builtin_stub!(Int64Array, OpaquePackedInt64Array); +impl_builtin_stub!(StringArray, OpaquePackedStringArray); +impl_builtin_stub!(Vector2Array, OpaquePackedVector2Array); +impl_builtin_stub!(Vector3Array, OpaquePackedVector3Array); + +impl_builtin_froms!(Array; + ByteArray => array_from_packed_byte_array, + ColorArray => array_from_packed_color_array, + Float32Array => array_from_packed_float32_array, + Float64Array => array_from_packed_float64_array, + Int32Array => array_from_packed_int32_array, + Int64Array => array_from_packed_int64_array, + StringArray => array_from_packed_string_array, + Vector2Array => array_from_packed_vector2_array, + Vector3Array => array_from_packed_vector3_array, +); + +impl_builtin_froms!(ByteArray; Array => packed_byte_array_from_array); +impl_builtin_froms!(ColorArray; Array => packed_color_array_from_array); +impl_builtin_froms!(Float32Array; Array => packed_float32_array_from_array); +impl_builtin_froms!(Float64Array; Array => packed_float64_array_from_array); +impl_builtin_froms!(Int32Array; Array => packed_int32_array_from_array); +impl_builtin_froms!(Int64Array; Array => packed_int64_array_from_array); +impl_builtin_froms!(StringArray; Array => packed_string_array_from_array); +impl_builtin_froms!(Vector2Array; Array => packed_vector2_array_from_array); +impl_builtin_froms!(Vector3Array; Array => packed_vector3_array_from_array); + +impl Array { + pub fn get(&self, index: i64) -> Option<&Variant> { + unsafe { + let ptr = (interface_fn!(array_operator_index))(self.sys(), index) as *mut Variant; + if ptr.is_null() { + return None; + } + Some(&*ptr) + } + } + + pub fn get_mut(&mut self, index: i64) -> Option<&mut Variant> { + unsafe { + let ptr = (interface_fn!(array_operator_index))(self.sys(), index) as *mut Variant; + if ptr.is_null() { + return None; + } + Some(&mut *ptr) + } + } +} + +impl Index for Array { + type Output = Variant; + + fn index(&self, index: i64) -> &Self::Output { + self.get(index).unwrap() // Godot will print error if index is OOB + } +} + +impl IndexMut for Array { + fn index_mut(&mut self, index: i64) -> &mut Self::Output { + self.get_mut(index).unwrap() // Godot will print error if index is OOB + } +} + +#[repr(C)] +pub struct TypedArray { + opaque: OpaqueArray, + _phantom: PhantomData, +} +impl TypedArray { + fn from_opaque(opaque: OpaqueArray) -> Self { + Self { + opaque, + _phantom: PhantomData, + } + } +} + +impl Clone for TypedArray { + fn clone(&self) -> Self { + unsafe { + Self::from_sys_init(|opaque_ptr| { + let ctor = sys::method_table().array_construct_copy; + ctor(opaque_ptr, &self.sys() as *const sys::GDNativeTypePtr); + }) + } + } +} +impl sys::GodotFfi for TypedArray { + ffi_methods! { type sys::GDNativeTypePtr = *mut Opaque; .. } +} +impl Drop for TypedArray { + fn drop(&mut self) { + unsafe { + let destructor = sys::method_table().array_destroy; + destructor(self.sys_mut()); + } + } +} + +impl TypedArray { + pub fn get(&self, index: i64) -> Option + where + for<'a> T: From<&'a Variant>, + { + unsafe { + let ptr = (interface_fn!(array_operator_index))(self.sys(), index); + let v = Variant::from_var_sys(ptr); + let ret = T::from(&v); + Some(ret) + } + } +} + +// // TODO: Is this possible to implement? Needs to return a reference. +// impl Index for TypedArray { +// type Output = Variant; +// +// fn index(&self, index: i64) -> &Self::Output { +// self.get(index).unwrap() // Godot will print error if index is OOB +// } +// } +// +// impl IndexMut for TypedArray { +// fn index_mut(&mut self, index: i64) -> &mut Self::Output { +// self.get_mut(index).unwrap() // Godot will print error if index is OOB +// } +// } diff --git a/gdext-builtin/src/lib.rs b/gdext-builtin/src/lib.rs index a87c35635..075d8feb2 100644 --- a/gdext-builtin/src/lib.rs +++ b/gdext-builtin/src/lib.rs @@ -8,19 +8,23 @@ pub mod macros; +mod arrays; mod color; mod others; mod string; mod variant; mod vector2; mod vector3; +mod vector4; +pub use arrays::*; pub use color::*; pub use others::*; pub use string::*; pub use variant::*; pub use vector2::*; pub use vector3::*; +pub use vector4::*; pub use glam; diff --git a/gdext-builtin/src/macros.rs b/gdext-builtin/src/macros.rs index ba4a46f3e..6e2269939 100644 --- a/gdext-builtin/src/macros.rs +++ b/gdext-builtin/src/macros.rs @@ -4,158 +4,192 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -#![macro_use] - -#[macro_export] -macro_rules! gdext_print_warning { - ($($args:tt),* $(,)?) => { - unsafe { - let msg = format!("{}\0", format_args!($($args,)*)); - - gdext_sys::interface_fn!(print_warning)( - msg.as_bytes().as_ptr() as *const _, - "\0".as_bytes().as_ptr() as *const _, - concat!(file!(), "\0").as_ptr() as *const _, - line!() as _, - ); - } - }; -} - -#[macro_export] -macro_rules! gdext_print_error { - ($($args:tt),* $(,)?) => { - unsafe { - let msg = format!("{}\0", format_args!($($args,)*)); - - gdext_sys::interface_fn!(print_error)( - msg.as_bytes().as_ptr() as *const _, - "\0".as_bytes().as_ptr() as *const _, - concat!(file!(), "\0").as_ptr() as *const _, - line!() as _, - ); - } - }; -} - -#[macro_export] -macro_rules! gdext_print_script_error { - ($($args:tt),* $(,)?) => { - unsafe { - let msg = format!("{}\0", format_args!($($args,)*)); - - gdext_sys::interface_fn!(print_script_error)( - msg.as_bytes().as_ptr() as *const _, - "\0".as_bytes().as_ptr() as *const _, - concat!(file!(), "\0").as_ptr() as *const _, - line!() as _, - ); - } - }; -} - -// ---------------------------------------------------------------------------------------------------------------------------------------------- - -macro_rules! impl_basic_trait_as_sys { - ( Drop for $Type:ty => $gd_method:ident ) => { - impl Drop for $Type { - #[inline] - fn drop(&mut self) { - unsafe { (get_api().$gd_method)(self.sys_mut()) } - } - } - }; - - ( Clone for $Type:ty => $gd_method:ident ) => { - impl Clone for $Type { - #[inline] - fn clone(&self) -> Self { - unsafe { - let mut result = sys::$GdType::default(); - (get_api().$gd_method)(&mut result, self.sys()); - <$Type>::from_sys(result) - } - } - } - }; - - ( Default for $Type:ty => $gd_method:ident ) => { - impl Default for $Type { - #[inline] - fn default() -> Self { - unsafe { - let mut gd_val = sys::$GdType::default(); - (get_api().$gd_method)(&mut gd_val); - <$Type>::from_sys(gd_val) - } - } - } - }; - - ( PartialEq for $Type:ty => $gd_method:ident ) => { - impl PartialEq for $Type { - #[inline] - fn eq(&self, other: &Self) -> bool { - unsafe { - let operator = gdext_sys::method_table().$gd_method; - - let mut result: bool = false; - operator(self.sys(), other.sys(), result.sys_mut()); - result - } - } - } - }; - - ( Eq for $Type:ty => $gd_method:ident ) => { - impl_basic_trait_as_sys!(PartialEq for $Type => $gd_method); - impl Eq for $Type {} - }; - - ( PartialOrd for $Type:ty => $gd_method:ident ) => { - impl PartialOrd for $Type { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - let op_less = |lhs, rhs| unsafe { - let operator = gdext_sys::method_table().$gd_method; - - let mut result: bool = false; - operator(lhs, rhs, result.sys_mut()); - result - }; - - if op_less(self.sys(), other.sys()) { - Some(std::cmp::Ordering::Less) - } else if op_less(other.sys(), self.sys()) { - Some(std::cmp::Ordering::Greater) - } else { - Some(std::cmp::Ordering::Equal) - } - } - } - }; - - ( Ord for $Type:ty => $gd_method:ident ) => { - impl_basic_trait_as_sys!(PartialOrd for $Type => $gd_method); - impl Ord for $Type { - #[inline] - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - PartialOrd::partial_cmp(self, other).expect("PartialOrd::partial_cmp") - } - } - }; -} - -macro_rules! impl_traits_as_sys { - ( - for $Type:ty { - $( $Trait:ident => $gd_method:ident; )* - } - ) => ( - $( - impl_basic_trait_as_sys!( - $Trait for $Type => $gd_method - ); - )* - ) -} +#![macro_use] + +#[macro_export] +macro_rules! gdext_print_warning { + ($($args:tt),* $(,)?) => { + unsafe { + let msg = format!("{}\0", format_args!($($args,)*)); + + gdext_sys::interface_fn!(print_warning)( + msg.as_bytes().as_ptr() as *const _, + "\0".as_bytes().as_ptr() as *const _, + concat!(file!(), "\0").as_ptr() as *const _, + line!() as _, + ); + } + }; +} + +#[macro_export] +macro_rules! gdext_print_error { + ($($args:tt),* $(,)?) => { + unsafe { + let msg = format!("{}\0", format_args!($($args,)*)); + + gdext_sys::interface_fn!(print_error)( + msg.as_bytes().as_ptr() as *const _, + "\0".as_bytes().as_ptr() as *const _, + concat!(file!(), "\0").as_ptr() as *const _, + line!() as _, + ); + } + }; +} + +#[macro_export] +macro_rules! gdext_print_script_error { + ($($args:tt),* $(,)?) => { + unsafe { + let msg = format!("{}\0", format_args!($($args,)*)); + + gdext_sys::interface_fn!(print_script_error)( + msg.as_bytes().as_ptr() as *const _, + "\0".as_bytes().as_ptr() as *const _, + concat!(file!(), "\0").as_ptr() as *const _, + line!() as _, + ); + } + }; +} + +// ---------------------------------------------------------------------------------------------------------------------------------------------- + +macro_rules! impl_basic_trait_as_sys { + ( Drop for $Type:ty => $gd_method:ident ) => { + impl Drop for $Type { + #[inline] + fn drop(&mut self) { + unsafe { (get_api().$gd_method)(self.sys_mut()) } + } + } + }; + + ( Clone for $Type:ty => $gd_method:ident ) => { + impl Clone for $Type { + #[inline] + fn clone(&self) -> Self { + unsafe { + let mut result = sys::$GdType::default(); + (get_api().$gd_method)(&mut result, self.sys()); + <$Type>::from_sys(result) + } + } + } + }; + + ( Default for $Type:ty => $gd_method:ident ) => { + impl Default for $Type { + #[inline] + fn default() -> Self { + unsafe { + let mut gd_val = sys::$GdType::default(); + (get_api().$gd_method)(&mut gd_val); + <$Type>::from_sys(gd_val) + } + } + } + }; + + ( PartialEq for $Type:ty => $gd_method:ident ) => { + impl PartialEq for $Type { + #[inline] + fn eq(&self, other: &Self) -> bool { + unsafe { + let operator = gdext_sys::method_table().$gd_method; + + let mut result: bool = false; + operator(self.sys(), other.sys(), result.sys_mut()); + result + } + } + } + }; + + ( Eq for $Type:ty => $gd_method:ident ) => { + impl_basic_trait_as_sys!(PartialEq for $Type => $gd_method); + impl Eq for $Type {} + }; + + ( PartialOrd for $Type:ty => $gd_method:ident ) => { + impl PartialOrd for $Type { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + let op_less = |lhs, rhs| unsafe { + let operator = gdext_sys::method_table().$gd_method; + + let mut result: bool = false; + operator(lhs, rhs, result.sys_mut()); + result + }; + + if op_less(self.sys(), other.sys()) { + Some(std::cmp::Ordering::Less) + } else if op_less(other.sys(), self.sys()) { + Some(std::cmp::Ordering::Greater) + } else { + Some(std::cmp::Ordering::Equal) + } + } + } + }; + + ( Ord for $Type:ty => $gd_method:ident ) => { + impl_basic_trait_as_sys!(PartialOrd for $Type => $gd_method); + impl Ord for $Type { + #[inline] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + PartialOrd::partial_cmp(self, other).expect("PartialOrd::partial_cmp") + } + } + }; +} + +macro_rules! impl_traits_as_sys { + ( + for $Type:ty { + $( $Trait:ident => $gd_method:ident; )* + } + ) => ( + $( + impl_basic_trait_as_sys!( + $Trait for $Type => $gd_method + ); + )* + ) +} + +macro_rules! impl_builtin_stub { + ($Class:ident, $OpaqueTy:ident) => { + #[repr(C)] + pub struct $Class { + opaque: sys::types::$OpaqueTy, + } + + impl $Class { + fn from_opaque(opaque: sys::types::$OpaqueTy) -> Self { + Self { opaque } + } + } + + impl GodotFfi for $Class { + ffi_methods! { type sys::GDNativeTypePtr = *mut Opaque; .. } + } + }; +} + +macro_rules! impl_builtin_froms { + ($To:ty; $($From:ty => $from_fn:ident),* $(,)?) => { + $(impl From<&$From> for $To { + fn from(other: &$From) -> Self { + unsafe { + Self::from_sys_init(|ptr| { + let converter = sys::method_table().$from_fn; + converter(ptr, &[other.sys()] as *const sys::GDNativeTypePtr); + }) + } + } + })* + }; +} diff --git a/gdext-builtin/src/others.rs b/gdext-builtin/src/others.rs index 3e7662851..2ccab4472 100644 --- a/gdext-builtin/src/others.rs +++ b/gdext-builtin/src/others.rs @@ -10,26 +10,16 @@ use crate::GodotString; use gdext_sys as sys; use sys::{ffi_methods, GodotFfi}; -macro_rules! impl_builtin_stub { - ($Class:ident, $OpaqueTy:ident) => { - #[repr(C)] - pub struct $Class { - opaque: sys::types::$OpaqueTy, - } - - impl $Class { - fn from_opaque(opaque: sys::types::$OpaqueTy) -> Self { - Self { opaque } - } - } - - impl GodotFfi for $Class { - ffi_methods! { type sys::GDNativeTypePtr = *mut Opaque; .. } - } - }; -} - -impl_builtin_stub!(Array, OpaqueArray); +// TODO: Swap more inner math types with glam types +impl_builtin_stub!(AABB, OpaqueAABB); +impl_builtin_stub!(Basis, OpaqueBasis); +impl_builtin_stub!(Plane, OpaquePlane); +impl_builtin_stub!(Quaternion, OpaqueQuaternion); +impl_builtin_stub!(Rect2, OpaqueRect2); +impl_builtin_stub!(Rect2i, OpaqueRect2i); + +impl_builtin_stub!(RID, OpaqueRID); +impl_builtin_stub!(Callable, OpaqueCallable); impl_builtin_stub!(Dictionary, OpaqueDictionary); impl_builtin_stub!(Transform2D, OpaqueTransform2D); impl_builtin_stub!(Transform3D, OpaqueTransform3D); diff --git a/gdext-builtin/src/vector2.rs b/gdext-builtin/src/vector2.rs index 57248f484..f646ac71a 100644 --- a/gdext-builtin/src/vector2.rs +++ b/gdext-builtin/src/vector2.rs @@ -45,3 +45,29 @@ impl std::fmt::Display for Vector2 { self.inner.fmt(f) } } + +type IInner = glam::IVec2; + +#[derive(Default, Copy, Clone, Debug)] +#[repr(C)] +pub struct Vector2i { + inner: IInner, +} + +impl Vector2i { + pub fn new(x: i32, y: i32) -> Self { + Self { + inner: IInner::new(x, y), + } + } +} + +impl GodotFfi for Vector2i { + ffi_methods! { type sys::GDNativeTypePtr = *mut Self; .. } +} + +impl std::fmt::Display for Vector2i { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner.fmt(f) + } +} diff --git a/gdext-builtin/src/vector3.rs b/gdext-builtin/src/vector3.rs index 6a4441468..7358b2c59 100644 --- a/gdext-builtin/src/vector3.rs +++ b/gdext-builtin/src/vector3.rs @@ -37,3 +37,29 @@ impl std::fmt::Display for Vector3 { self.inner.fmt(f) } } + +type IInner = glam::IVec3; + +#[derive(Default, Copy, Clone, Debug)] +#[repr(C)] +pub struct Vector3i { + inner: IInner, +} + +impl Vector3i { + pub fn new(x: i32, y: i32, z: i32) -> Self { + Self { + inner: IInner::new(x, y, z), + } + } +} + +impl GodotFfi for Vector3i { + ffi_methods! { type sys::GDNativeTypePtr = *mut Self; .. } +} + +impl std::fmt::Display for Vector3i { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner.fmt(f) + } +} diff --git a/gdext-builtin/src/vector4.rs b/gdext-builtin/src/vector4.rs new file mode 100644 index 000000000..6ab228784 --- /dev/null +++ b/gdext-builtin/src/vector4.rs @@ -0,0 +1,63 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use gdext_sys as sys; +use sys::{ffi_methods, real, GodotFfi}; + +//#[cfg(not(feature = "real_is_double"))] +type Inner = glam::f32::Vec4; +// #[cfg(feature = "real_is_double")] +// type Inner = glam::f64::DVec4; + +#[derive(Default, Copy, Clone, Debug, PartialEq)] +#[repr(C)] +pub struct Vector4 { + inner: Inner, +} + +impl Vector4 { + pub fn new(x: real, y: real, z: real, w: real) -> Self { + Self { + inner: Inner::new(x, y, z, w), + } + } +} + +impl GodotFfi for Vector4 { + ffi_methods! { type sys::GDNativeTypePtr = *mut Self; .. } +} + +impl std::fmt::Display for Vector4 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner.fmt(f) + } +} + +type IInner = glam::IVec4; + +#[derive(Default, Copy, Clone, Debug)] +#[repr(C)] +pub struct Vector4i { + inner: IInner, +} + +impl Vector4i { + pub fn new(x: i32, y: i32, z: i32, w: i32) -> Self { + Self { + inner: IInner::new(x, y, z, w), + } + } +} + +impl GodotFfi for Vector4i { + ffi_methods! { type sys::GDNativeTypePtr = *mut Self; .. } +} + +impl std::fmt::Display for Vector4i { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner.fmt(f) + } +} diff --git a/gdext-codegen/src/central_generator.rs b/gdext-codegen/src/central_generator.rs index b4c1e6bc5..8beaa3e3f 100644 --- a/gdext-codegen/src/central_generator.rs +++ b/gdext-codegen/src/central_generator.rs @@ -34,6 +34,15 @@ struct TypeNames { sys_variant_type: Ident, } +/// Allows collecting all builtin TypeNames before generating methods +struct BuiltinTypeInfo<'a> { + value: i32, + type_names: TypeNames, + has_destructor: bool, + constructors: Option<&'a Vec>, + operators: Option<&'a Vec>, +} + pub(crate) fn generate_central_file( api: &ExtensionApi, _ctx: &Context, @@ -105,12 +114,15 @@ fn make_central_items(api: &ExtensionApi, build_config: &str) -> CentralItems { let class_map = class_map; + let mut builtin_types_map = HashMap::new(); + // Find enum with variant types, and generate mapping code let mut variant_enumerators = vec![]; let mut variant_fn_decls = vec![]; let mut variant_fn_inits = vec![]; for enum_ in &api.global_enums { if &enum_.name == "Variant.Type" { + // Collect all `BuiltinTypeInfo`s for ty in &enum_.values { let shout_case = ty .name @@ -154,10 +166,30 @@ fn make_central_items(api: &ExtensionApi, build_config: &str) -> CentralItems { }; let value = ty.value; - variant_enumerators.push(make_enumerator(&type_names, value)); - let (decl, init) = - make_variant_fns(&type_names, has_destructor, constructors, operators); + builtin_types_map.insert( + type_names.pascal_case.clone(), + BuiltinTypeInfo { + value, + type_names, + has_destructor, + constructors, + operators, + }, + ); + } + + // Generate builtin methods, now with info for all types available + for ty in builtin_types_map.values() { + variant_enumerators.push(make_enumerator(&ty.type_names, ty.value)); + + let (decl, init) = make_variant_fns( + &ty.type_names, + ty.has_destructor, + ty.constructors, + ty.operators, + &builtin_types_map, + ); variant_fn_decls.push(decl); variant_fn_inits.push(init); @@ -200,8 +232,10 @@ fn make_variant_fns( has_destructor: bool, constructors: Option<&Vec>, operators: Option<&Vec>, + builtin_types: &HashMap, ) -> (TokenStream, TokenStream) { - let (construct_decls, construct_inits) = make_construct_fns(&type_names, constructors); + let (construct_decls, construct_inits) = + make_construct_fns(&type_names, constructors, builtin_types); let (destroy_decls, destroy_inits) = make_destroy_fns(&type_names, has_destructor); let (op_eq_decls, op_eq_inits) = make_operator_fns(&type_names, operators, "==", "EQUAL"); let (op_lt_decls, op_lt_inits) = make_operator_fns(&type_names, operators, "<", "LESS"); @@ -247,6 +281,7 @@ fn make_variant_fns( fn make_construct_fns( type_names: &TypeNames, constructors: Option<&Vec>, + builtin_types: &HashMap, ) -> (TokenStream, TokenStream) { let constructors = match constructors { Some(c) => c, @@ -261,7 +296,7 @@ fn make_construct_fns( // [0]: default constructor // [1]: copy constructor // [2]: (optional) typically the most common conversion constructor (e.g. StringName -> String) - // rest: not interesting for now + // rest: (optional) other conversion constructors and multi-arg constructors (e.g. Vector3(x, y, z)) // Sanity checks -- ensure format is as expected for (i, c) in constructors.iter().enumerate() { @@ -287,16 +322,14 @@ fn make_construct_fns( let construct_copy_error = format_load_error(&construct_copy); let variant_type = &type_names.sys_variant_type; - let (construct_extra, construct_extra_index, construct_extra_error) = - make_extra_constructors(type_names); + let (construct_extra_decls, construct_extra_inits) = + make_extra_constructors(type_names, constructors, builtin_types); // Generic signature: fn(base: GDNativeTypePtr, args: *const GDNativeTypePtr) let decls = quote! { pub #construct_default: unsafe extern "C" fn(GDNativeTypePtr, *const GDNativeTypePtr), pub #construct_copy: unsafe extern "C" fn(GDNativeTypePtr, *const GDNativeTypePtr), - #( - pub #construct_extra: unsafe extern "C" fn(GDNativeTypePtr, *const GDNativeTypePtr), - )* + #(#construct_extra_decls)* }; let inits = quote! { @@ -308,45 +341,52 @@ fn make_construct_fns( let ctor_fn = interface.variant_get_ptr_constructor.unwrap(); ctor_fn(crate:: #variant_type, 1i32).expect(#construct_copy_error) }, - #( - #construct_extra: { - let ctor_fn = interface.variant_get_ptr_constructor.unwrap(); - ctor_fn(crate:: #variant_type, #construct_extra_index).expect(#construct_extra_error) - }, - )* + #(#construct_extra_inits)* }; (decls, inits) } /// Lists special cases for useful constructors -fn make_extra_constructors(type_names: &TypeNames) -> (Vec, Vec, Vec) { - // TODO instead of finding ctors by index, more robust would be to look up from JSON - let one_vec = match type_names.pascal_case.as_str() { - "NodePath" => { - vec![("node_path_from_string", 2)] - } - "StringName" => { - vec![("string_name_from_string", 2)] - } - "String" => { - vec![ - ("string_from_string_name", 2), // - ("string_from_node_path", 3), - ] +fn make_extra_constructors( + type_names: &TypeNames, + constructors: &Vec, + builtin_types: &HashMap, +) -> (Vec, Vec) { + let mut extra_decls = Vec::with_capacity(constructors.len() - 2); + let mut extra_inits = Vec::with_capacity(constructors.len() - 2); + let variant_type = &type_names.sys_variant_type; + for i in 2..constructors.len() { + let ctor = &constructors[i]; + if let Some(args) = &ctor.arguments { + let type_name = &type_names.snake_case; + let ident = if args.len() == 1 && args[0].name == "from" { + // Conversion constructor is named according to the source type + // String(NodePath from) => string_from_node_path + let arg_type = &builtin_types[&args[0].type_].type_names.snake_case; + format_ident!("{type_name}_from_{arg_type}") + } else { + // Type-specific constructor is named according to the argument names + // Vector3(float x, float y, float z) => vector3_from_x_y_z + let arg_names = args + .iter() + .fold(String::new(), |acc, arg| acc + &arg.name + "_"); + format_ident!("{type_name}_from_{arg_names}") + }; + let err = format_load_error(&ident); + extra_decls.push(quote! { + pub #ident: unsafe extern "C" fn(GDNativeTypePtr, *const GDNativeTypePtr), + }); + let i = i as i32; + extra_inits.push(quote! { + #ident: { + let ctor_fn = interface.variant_get_ptr_constructor.unwrap(); + ctor_fn(crate:: #variant_type, #i).expect(#err) + }, + }); } - _ => vec![], - }; - - // unzip() for 3 - ( - one_vec.iter().map(|(name, _)| ident(name)).collect(), - one_vec.iter().map(|(_, index)| *index).collect(), - one_vec - .iter() - .map(|(name, _)| format_load_error(name)) - .collect(), - ) + } + (extra_decls, extra_inits) } fn make_destroy_fns(type_names: &TypeNames, has_destructor: bool) -> (TokenStream, TokenStream) { diff --git a/gdext-codegen/src/class_generator.rs b/gdext-codegen/src/class_generator.rs index 4d3ebbcc7..f6f890e75 100644 --- a/gdext-codegen/src/class_generator.rs +++ b/gdext-codegen/src/class_generator.rs @@ -391,26 +391,61 @@ fn make_utility_return(return_value: &Option, ctx: &Context) -> (TokenSt fn to_rust_type(ty: &str, ctx: &Context) -> RustTy { //println!("to_rust_ty: {ty}"); - if let Some(remain) = ty.strip_prefix("enum::") { - let mut parts = remain.split("."); - - let first = parts.next().unwrap(); - let ident = match parts.next() { - Some(second) => { - // enum::Animation.LoopMode - format_ident!("{}{}", first, second) // TODO better - } - None => { - // enum::Error - format_ident!("{}", first) - } - }; - - assert!(parts.next().is_none(), "Unrecognized enum type '{}'", ty); + // if let Some(remain) = ty.strip_prefix("enum::") { + // let mut parts = remain.split("."); + // + // let first = parts.next().unwrap(); + // let ident = match parts.next() { + // Some(second) => { + // // enum::Animation.LoopMode + // format_ident!("{}{}", first, second) // TODO better + // } + // None => { + // // enum::Error + // format_ident!("{}", first) + // } + // }; + // + // assert!(parts.next().is_none(), "Unrecognized enum type '{}'", ty); + // return RustTy { + // tokens: ident.to_token_stream(), + // is_engine_class: false, + // }; + // } + + // TODO: newtypes for enums & bitfields? + // - more verbose to use and complicated to implement + // - lack of inherent associated types makes module structure awkward + // - need to implement bitwise traits for bitfields + // - API breaks often in Godot + // - prior art = used i64 constants for gdnative + // + but type safety! + if ty.starts_with("bitfield::") || ty.starts_with("enum::") { return RustTy { - tokens: ident.to_token_stream(), + tokens: ident("i32").to_token_stream(), is_engine_class: false, }; + } else if let Some(packed_arr_ty) = ty.strip_prefix("Packed") { + // Don't trigger on PackedScene ;P + if packed_arr_ty.ends_with("Array") { + return RustTy { + tokens: ident(packed_arr_ty).to_token_stream(), + is_engine_class: false, + }; + } + } else if let Some(arr_ty) = ty.strip_prefix("typedarray::") { + return if let Some(packed_arr_ty) = arr_ty.strip_prefix("Packed") { + RustTy { + tokens: ident(packed_arr_ty).to_token_stream(), + is_engine_class: false, + } + } else { + let arr_ty = to_rust_type(arr_ty, ctx).tokens; + RustTy { + tokens: quote!(TypedArray<#arr_ty>), + is_engine_class: false, + } + }; } if ctx.is_engine_class(ty) { @@ -428,6 +463,7 @@ fn to_rust_type(ty: &str, ctx: &Context) -> RustTy { "int" => "i64", "float" => "f64", "String" => "GodotString", + "Error" => "GodotError", other => other, };