diff --git a/Cargo.lock b/Cargo.lock index d3e84d2..243219e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ checksum = "aca749d3d3f5b87a0d6100509879f9cf486ab510803a4a4e1001da1ff61c2bd6" [[package]] name = "darling" -version = "0.20.3" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -35,9 +35,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", @@ -49,9 +49,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", @@ -345,9 +345,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" diff --git a/Cargo.toml b/Cargo.toml index ab736a6..49f51de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ godot = { git = "https://github.com/godot-rust/gdext", tag = "v0.1.3", features godot-cell = { git = "https://github.com/godot-rust/gdext", tag = "v0.1.3" } itertools = "0.10.3" rand = "0.8.5" -darling = { version = "0.20.3" } +darling = { version = "0.20.10" } proc-macro2 = "1.0.68" quote = "1.0.33" syn = "2.0.38" diff --git a/derive/src/attribute_ops.rs b/derive/src/attribute_ops.rs index 2fb6aa0..69c988a 100644 --- a/derive/src/attribute_ops.rs +++ b/derive/src/attribute_ops.rs @@ -4,66 +4,74 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use darling::{ast::Data, util, FromAttributes, FromDeriveInput, FromField, FromMeta}; +use darling::{ + ast::Data, + util::{self, WithOriginal}, + FromAttributes, FromDeriveInput, FromField, FromMeta, +}; use proc_macro2::{Span, TokenStream}; -use quote::quote; +use quote::{quote, quote_spanned, ToTokens}; +use syn::{spanned::Spanned, LitStr, Meta, Type}; use crate::type_paths::godot_types; #[derive(FromAttributes, Debug)] #[darling(attributes(export))] pub struct FieldExportOps { - #[darling(default)] - color_no_alpha: bool, - #[darling(default)] - dir: bool, - exp_easing: Option, - file: Option, - enum_options: Option, - flags: Option, - #[darling(default)] - global_dir: bool, - #[darling(default)] - global_file: bool, - #[darling(default)] - multiline: bool, - node_path: Option, - placeholder: Option, - range: Option, + color_no_alpha: Option>, + dir: Option>, + exp_easing: Option>, + file: Option>, + enum_options: Option>, + flags: Option>, + global_dir: Option>, + global_file: Option>, + multiline: Option>, + node_path: Option>, + placeholder: Option>, + range: Option>, + #[darling(rename = "ty")] + custom_type: Option>, } impl FieldExportOps { - pub fn hint(&self, span: Span) -> Result<(TokenStream, String), TokenStream> { + pub fn hint(&self, ty: &Type) -> Result<(TokenStream, TokenStream), TokenStream> { let godot_types = godot_types(); let property_hints = quote!(#godot_types::global::PropertyHint); - let mut result: Option<(&str, TokenStream, String)> = None; - if self.color_no_alpha { + let mut result: Option<(&str, TokenStream, TokenStream)> = None; + + if let Some(color_no_alpha) = self.color_no_alpha.as_ref() { result = Some(( "color_no_alpha", - quote!(#property_hints::COLOR_NO_ALPHA), - String::new(), + quote_spanned!(color_no_alpha.original.span() => #property_hints::COLOR_NO_ALPHA), + "".to_token_stream(), )); } - if self.dir { + if let Some(dir) = self.dir.as_ref() { let field = "dir"; if let Some((active_field, _, _)) = result { - return Self::error(span, active_field, field); + return Self::error(dir.original.span(), active_field, field); } - result = Some((field, quote!(#property_hints::DIR), String::new())); + result = Some(( + field, + quote_spanned!(dir.original.span() => #property_hints::DIR), + "".to_token_stream(), + )); } if let Some(exp_list) = self.exp_easing.as_ref() { let field = "exp_easing"; if let Some((active_field, _, _)) = result { - return Self::error(span, active_field, field); + return Self::error(exp_list.original.span(), active_field, field); } let parsed_params = exp_list + .parsed .elems .iter() .map(ExpEasingOpts::from_expr) @@ -80,8 +88,8 @@ impl FieldExportOps { result = Some(( field, - quote!(#property_hints::EXP_EASING), - serialized_params.join(","), + quote_spanned!(exp_list.original.span() => #property_hints::EXP_EASING), + serialized_params.join(",").to_token_stream(), )); } @@ -89,101 +97,131 @@ impl FieldExportOps { let field = "file"; if let Some((active_field, _, _)) = result { - return Self::error(span, active_field, field); + return Self::error(list.original.span(), active_field, field); } let filters = list + .parsed .elems .iter() .map(String::from_expr) .collect::, _>>() .map_err(|err| err.write_errors())?; - result = Some((field, quote!(#property_hints::FILE), filters.join(","))); + result = Some(( + field, + quote_spanned!(list.original.span() => #property_hints::FILE), + filters.join(",").to_token_stream(), + )); } if let Some(list) = self.enum_options.as_ref() { - let field = "enum"; + let field = "enum_options"; if let Some((active_field, _, _)) = result { - return Self::error(span, active_field, field); + return Self::error(list.original.span(), active_field, field); } let flags = list + .parsed .elems .iter() .map(String::from_expr) .collect::, _>>() - .map_err(|err| err.write_errors())?; + .map_err(|err| err.write_errors())? + .join(","); - result = Some((field, quote!(#property_hints::ENUM), flags.join(","))); + result = Some(( + field, + quote_spanned!(list.original.span() => #property_hints::ENUM), + quote_spanned!(list.original.span() => String::from(#flags)), + )); } if let Some(list) = self.flags.as_ref() { let field = "flags"; if let Some((active_field, _, _)) = result { - return Self::error(span, active_field, field); + return Self::error(list.original.span(), active_field, field); } let flags = list + .parsed .elems .iter() .map(String::from_expr) .collect::, _>>() .map_err(|err| err.write_errors())?; - result = Some((field, quote!(#property_hints::FLAGS), flags.join(","))); + result = Some(( + field, + quote_spanned!(list.original.span() => #property_hints::FLAGS), + flags.join(",").to_token_stream(), + )); } - if self.global_dir { + if let Some(global_dir) = self.global_dir.as_ref() { let field = "global_dir"; if let Some((active_field, _, _)) = result { - return Self::error(span, active_field, field); + return Self::error(global_dir.original.span(), active_field, field); } - result = Some((field, quote!(#property_hints::GLOBAL_DIR), String::new())); + result = Some(( + field, + quote_spanned!(global_dir.original.span() => #property_hints::GLOBAL_DIR), + "".to_token_stream(), + )); } - if self.global_file { + if let Some(global_file) = self.global_file.as_ref() { let field = "global_file"; if let Some((active_field, _, _)) = result { - return Self::error(span, active_field, field); + return Self::error(global_file.original.span(), active_field, field); } - result = Some((field, quote!(#property_hints::GLOBAL_FILE), String::new())); + result = Some(( + field, + quote_spanned!(global_file.original.span() => #property_hints::GLOBAL_FILE), + String::new().to_token_stream(), + )); } - if self.multiline { + if let Some(multiline) = self.multiline.as_ref() { let field = "multiline"; if let Some((active_field, _, _)) = result { - return Self::error(span, active_field, field); + return Self::error(multiline.original.span(), active_field, field); } - result = Some((field, quote!(#property_hints::MULTILINE), String::new())); + result = Some(( + field, + quote_spanned!(multiline.original.span() => #property_hints::MULTILINE), + "".to_token_stream(), + )); } if let Some(list) = self.node_path.as_ref() { let field = "node_path"; if let Some((active_field, _, _)) = result { - return Self::error(span, active_field, field); + return Self::error(list.original.span(), active_field, field); } let types = list + .parsed .elems .iter() .map(String::from_expr) .collect::, _>>() - .map_err(|err| err.write_errors())?; + .map_err(|err| err.write_errors())? + .join(","); result = Some(( field, - quote!(#property_hints::NODE_PATH_VALID_TYPES), - types.join(","), + quote_spanned!(list.original.span() => #property_hints::NODE_PATH_VALID_TYPES), + quote_spanned!(list.original.span() => String::from(#types)), )); } @@ -191,13 +229,13 @@ impl FieldExportOps { let field = "placeholder"; if let Some((active_field, _, _)) = result { - return Self::error(span, active_field, field); + return Self::error(text.original.span(), active_field, field); } result = Some(( field, - quote!(#property_hints::PLACEHOLDER_TEXT), - text.to_owned(), + quote_spanned!(text.original.span() => #property_hints::PLACEHOLDER_TEXT), + text.parsed.to_token_stream(), )); } @@ -205,21 +243,41 @@ impl FieldExportOps { let field = "range"; if let Some((active_field, _, _)) = result { - return Self::error(span, active_field, field); + return Self::error(ops.original.span(), active_field, field); } - let step = ops.step.unwrap_or(1.0); + let step = ops.parsed.step.unwrap_or(1.0); result = Some(( field, - quote!(#property_hints::RANGE), - format!("{},{},{}", ops.min, ops.max, step), + quote_spanned!(ops.original.span() => #property_hints::RANGE), + format!("{},{},{}", ops.parsed.min, ops.parsed.max, step).to_token_stream(), )); } + if let Some(attr_ty) = self.custom_type.as_ref() { + let field = "ty"; + + if let Some((active_field, _, _)) = result { + return Self::error(attr_ty.original.span(), active_field, field); + } + + let attr_ty_raw = &attr_ty.parsed; + + let hint = quote_spanned!(ty.span() => <#ty as ::godot_rust_script::GodotScriptExport>::hint()); + let hint_string = quote_spanned!(attr_ty.original.span() => String::from(#attr_ty_raw)); + + result = Some((field, hint, hint_string)); + } + let result = result .map(|(_, tokens, hint_string)| (tokens, hint_string)) - .unwrap_or_else(|| (quote!(#property_hints::NONE), String::new())); + .unwrap_or_else(|| { + let default_hint = quote_spanned!(ty.span() => <#ty as ::godot_rust_script::GodotScriptExport>::hint()); + let default_hint_string = quote_spanned!(ty.span() => <#ty as ::godot_rust_script::GodotScriptExport>::hint_string()); + + (default_hint, default_hint_string) + }); Ok(result) } @@ -228,10 +286,10 @@ impl FieldExportOps { span: Span, active_field: &str, field: &str, - ) -> Result<(TokenStream, String), TokenStream> { + ) -> Result<(TokenStream, TokenStream), TokenStream> { let err = syn::Error::new( span, - format!("{} is not compatible with {}", active_field, field), + format!("{} is not compatible with {}", field, active_field), ) .into_compile_error(); diff --git a/derive/src/impl_attribute.rs b/derive/src/impl_attribute.rs index f5589d7..7a4e107 100644 --- a/derive/src/impl_attribute.rs +++ b/derive/src/impl_attribute.rs @@ -12,7 +12,7 @@ use syn::{ }; use crate::{ - compile_error, is_context_type, rust_to_variant_type, + extract_ident_from_type, is_context_type, rust_to_variant_type, type_paths::{godot_types, property_hints, string_name_ty, variant_ty}, }; @@ -74,7 +74,7 @@ pub fn godot_script_impl( ty: #arg_type, exported: false, hint: #property_hints::NONE, - hint_string: "", + hint_string: String::new(), description: "", }, }, @@ -134,7 +134,7 @@ pub fn godot_script_impl( ty: #fn_return_ty, exported: false, hint: #property_hints::NONE, - hint_string: "", + hint_string: String::new(), description: "", }, flags: #method_flag, @@ -190,36 +190,6 @@ pub fn godot_script_impl( .into() } -fn extract_script_name_from_type(impl_target: &syn::Type) -> Result { - match impl_target { - Type::Array(_) => Err(compile_error("Arrays are not supported!", impl_target)), - Type::BareFn(_) => Err(compile_error( - "Bare functions are not supported!", - impl_target, - )), - Type::Group(_) => Err(compile_error("Groups are not supported!", impl_target)), - Type::ImplTrait(_) => Err(compile_error("Impl traits are not suppored!", impl_target)), - Type::Infer(_) => Err(compile_error("Infer is not supported!", impl_target)), - Type::Macro(_) => Err(compile_error("Macro types are not supported!", impl_target)), - Type::Never(_) => Err(compile_error("Never type is not supported!", impl_target)), - Type::Paren(_) => Err(compile_error("Unsupported type!", impl_target)), - Type::Path(ref path) => Ok(path.path.segments.last().unwrap().ident.clone()), - Type::Ptr(_) => Err(compile_error( - "Pointer types are not supported!", - impl_target, - )), - Type::Reference(_) => Err(compile_error("References are not supported!", impl_target)), - Type::Slice(_) => Err(compile_error("Slices are not supported!", impl_target)), - Type::TraitObject(_) => Err(compile_error( - "Trait objects are not supported!", - impl_target, - )), - Type::Tuple(_) => Err(compile_error("Tuples are not supported!", impl_target)), - Type::Verbatim(_) => Err(compile_error("Verbatim is not supported!", impl_target)), - _ => Err(compile_error("Unsupported type!", impl_target)), - } -} - fn sanitize_trait_fn_arg(arg: FnArg) -> FnArg { match arg { FnArg::Receiver(mut rec) => { @@ -264,7 +234,7 @@ fn sanitize_trait_fn_arg(arg: FnArg) -> FnArg { fn generate_public_interface(impl_body: &ItemImpl) -> TokenStream { let impl_target = impl_body.self_ty.as_ref(); - let script_name = match extract_script_name_from_type(impl_target) { + let script_name = match extract_ident_from_type(impl_target) { Ok(target) => target, Err(err) => return err, }; diff --git a/derive/src/lib.rs b/derive/src/lib.rs index d2e840f..e809fe5 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -9,10 +9,10 @@ mod impl_attribute; mod type_paths; use attribute_ops::{FieldOpts, GodotScriptOpts}; -use darling::{FromAttributes, FromDeriveInput}; +use darling::{ FromAttributes, FromDeriveInput}; use proc_macro2::TokenStream; use quote::{quote, quote_spanned, ToTokens}; -use syn::{parse_macro_input, spanned::Spanned, DeriveInput}; +use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Ident, Type}; use type_paths::{godot_types, string_name_ty, variant_ty}; use crate::attribute_ops::{FieldExportOps, PropertyOpts}; @@ -67,7 +67,7 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ops = FieldExportOps::from_attributes(&field.attrs) .map_err(|err| err.write_errors())?; - ops.hint(field.ident.span())? + ops.hint(&field.ty)? }; let description = get_field_description(field); @@ -390,3 +390,33 @@ pub fn godot_script_impl( fn compile_error(message: &str, tokens: impl ToTokens) -> TokenStream { syn::Error::new_spanned(tokens, message).into_compile_error() } + +fn extract_ident_from_type(impl_target: &syn::Type) -> Result { + match impl_target { + Type::Array(_) => Err(compile_error("Arrays are not supported!", impl_target)), + Type::BareFn(_) => Err(compile_error( + "Bare functions are not supported!", + impl_target, + )), + Type::Group(_) => Err(compile_error("Groups are not supported!", impl_target)), + Type::ImplTrait(_) => Err(compile_error("Impl traits are not suppored!", impl_target)), + Type::Infer(_) => Err(compile_error("Infer is not supported!", impl_target)), + Type::Macro(_) => Err(compile_error("Macro types are not supported!", impl_target)), + Type::Never(_) => Err(compile_error("Never type is not supported!", impl_target)), + Type::Paren(_) => Err(compile_error("Unsupported type!", impl_target)), + Type::Path(ref path) => Ok(path.path.segments.last().unwrap().ident.clone()), + Type::Ptr(_) => Err(compile_error( + "Pointer types are not supported!", + impl_target, + )), + Type::Reference(_) => Err(compile_error("References are not supported!", impl_target)), + Type::Slice(_) => Err(compile_error("Slices are not supported!", impl_target)), + Type::TraitObject(_) => Err(compile_error( + "Trait objects are not supported!", + impl_target, + )), + Type::Tuple(_) => Err(compile_error("Tuples are not supported!", impl_target)), + Type::Verbatim(_) => Err(compile_error("Verbatim is not supported!", impl_target)), + _ => Err(compile_error("Unsupported type!", impl_target)), + } +} diff --git a/rust-script/src/interface.rs b/rust-script/src/interface.rs index 9504899..0b28965 100644 --- a/rust-script/src/interface.rs +++ b/rust-script/src/interface.rs @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +mod export; mod signals; use std::marker::PhantomData; @@ -15,6 +16,7 @@ use godot::prelude::{Gd, Object, StringName, Variant}; pub use crate::runtime::Context; +pub use export::GodotScriptExport; pub use signals::{ScriptSignal, Signal}; pub trait GodotScript: Debug + GodotScriptImpl { diff --git a/rust-script/src/interface/export.rs b/rust-script/src/interface/export.rs new file mode 100644 index 0000000..99e29ed --- /dev/null +++ b/rust-script/src/interface/export.rs @@ -0,0 +1,143 @@ +/* + * 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 http://mozilla.org/MPL/2.0/. + */ + +#[cfg(since_api = "4.3")] +use godot::builtin::PackedVector4Array; +use godot::builtin::{ + Aabb, Array, Basis, Callable, Color, Dictionary, GString, NodePath, PackedByteArray, + PackedColorArray, PackedFloat32Array, PackedFloat64Array, PackedInt32Array, PackedInt64Array, + PackedStringArray, PackedVector2Array, PackedVector3Array, Plane, Projection, Quaternion, + Rect2, Rect2i, Rid, StringName, Transform2D, Transform3D, Vector2, Vector2i, Vector3, Vector3i, + Vector4, Vector4i, +}; +use godot::engine::{Node, Resource}; +use godot::global::PropertyHint; +use godot::meta::{ArrayElement, GodotType}; +use godot::obj::{EngineEnum, Gd}; +use godot::prelude::GodotClass; +use godot::sys::GodotFfi; + +pub trait GodotScriptExport { + fn hint_string() -> String; + + fn hint() -> PropertyHint; +} + +impl GodotScriptExport for Gd { + fn hint_string() -> String { + T::class_name().as_str().to_owned() + } + + fn hint() -> PropertyHint { + if T::inherits::() { + PropertyHint::NODE_TYPE + } else if T::inherits::() { + PropertyHint::RESOURCE_TYPE + } else { + PropertyHint::NONE + } + } +} + +impl GodotScriptExport for Option { + fn hint_string() -> String { + T::hint_string() + } + + fn hint() -> PropertyHint { + T::hint() + } +} + +impl GodotScriptExport for Array { + fn hint_string() -> String { + let element_type = <::Ffi as GodotFfi>::variant_type().ord(); + let element_hint = ::hint().ord(); + let element_hint_string = ::hint_string(); + + format!("{}/{}:{}", element_type, element_hint, element_hint_string) + } + + fn hint() -> PropertyHint { + PropertyHint::ARRAY_TYPE + } +} + +macro_rules! default_export { + ($ty:ty) => { + impl GodotScriptExport for $ty { + fn hint_string() -> String { + String::new() + } + + fn hint() -> PropertyHint { + PropertyHint::NONE + } + } + }; +} + +// Bounding Boxes +default_export!(Aabb); +default_export!(Rect2); +default_export!(Rect2i); + +// Matrices +default_export!(Basis); +default_export!(Transform2D); +default_export!(Transform3D); +default_export!(Projection); + +// Vectors +default_export!(Vector2); +default_export!(Vector2i); +default_export!(Vector3); +default_export!(Vector3i); +default_export!(Vector4); +default_export!(Vector4i); + +// Misc Math +default_export!(Quaternion); +default_export!(Plane); + +// Stringy Types +default_export!(GString); +default_export!(StringName); +default_export!(NodePath); + +default_export!(Color); + +// Arrays +default_export!(PackedByteArray); +default_export!(PackedInt32Array); +default_export!(PackedInt64Array); +default_export!(PackedFloat32Array); +default_export!(PackedFloat64Array); +default_export!(PackedStringArray); +default_export!(PackedVector2Array); +default_export!(PackedVector3Array); +#[cfg(since_api = "4.3")] +default_export!(PackedVector4Array); +default_export!(PackedColorArray); + +// Primitives +default_export!(f64); +default_export!(i64); +default_export!(bool); +default_export!(f32); + +default_export!(i32); +default_export!(i16); +default_export!(i8); +default_export!(u32); +default_export!(u16); +default_export!(u8); + +default_export!(Callable); +default_export!(godot::builtin::Signal); +default_export!(Dictionary); + +default_export!(Rid); diff --git a/rust-script/src/interface/signals.rs b/rust-script/src/interface/signals.rs index 02f40ec..374dcb8 100644 --- a/rust-script/src/interface/signals.rs +++ b/rust-script/src/interface/signals.rs @@ -126,7 +126,7 @@ macro_rules! signal_argument_desc { ty: <<<$type as GodotConvert>::Via as GodotType>::Ffi as godot::sys::GodotFfi>::variant_type(), exported: false, hint: PropertyHint::NONE, - hint_string: "", + hint_string: String::new(), description: "", } }; diff --git a/rust-script/src/static_script_registry.rs b/rust-script/src/static_script_registry.rs index 9cd946a..3f16b0b 100644 --- a/rust-script/src/static_script_registry.rs +++ b/rust-script/src/static_script_registry.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use godot::builtin::{GString, StringName}; use godot::global::{MethodFlags, PropertyHint, PropertyUsageFlags}; -use godot::meta::{ClassName, MethodInfo, PropertyInfo}; +use godot::meta::{ClassName, MethodInfo, PropertyInfo, ToGodot}; use godot::obj::{EngineBitfield, EngineEnum}; use godot::prelude::{Gd, Object}; use godot::sys::VariantType; @@ -82,7 +82,7 @@ pub struct RustScriptPropDesc { pub ty: VariantType, pub exported: bool, pub hint: PropertyHint, - pub hint_string: &'static str, + pub hint_string: String, pub description: &'static str, } @@ -98,7 +98,7 @@ impl RustScriptPropDesc { PropertyUsageFlags::NONE.ord() }, hint: self.hint.ord(), - hint_string: self.hint_string, + hint_string: self.hint_string.clone(), description: self.description, } } @@ -212,7 +212,7 @@ pub struct RustScriptPropertyInfo { pub property_name: &'static str, pub class_name: &'static str, pub hint: i32, - pub hint_string: &'static str, + pub hint_string: String, pub usage: u64, pub description: &'static str, } @@ -224,7 +224,7 @@ impl From<&RustScriptPropertyInfo> for PropertyInfo { property_name: value.property_name.into(), class_name: ClassName::from_ascii_cstr(value.class_name.as_bytes()), hint: PropertyHint::try_from_ord(value.hint).unwrap_or(PropertyHint::NONE), - hint_string: value.hint_string.into(), + hint_string: value.hint_string.to_godot(), usage: PropertyUsageFlags::try_from_ord(value.usage) .unwrap_or(PropertyUsageFlags::NONE), } diff --git a/rust-script/tests/script_derive.rs b/rust-script/tests/script_derive.rs index b742b08..002b484 100644 --- a/rust-script/tests/script_derive.rs +++ b/rust-script/tests/script_derive.rs @@ -4,8 +4,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use godot::builtin::{GString, StringName}; -use godot::classes::Node; +use godot::builtin::{Array, GString, StringName}; +use godot::classes::{Node, Node3D}; use godot::obj::{Gd, NewAlloc}; use godot_rust_script::{godot_script_impl, Context, GodotScript, Signal}; @@ -13,6 +13,7 @@ use godot_rust_script::{godot_script_impl, Context, GodotScript, Signal}; #[script(base = Node)] struct TestScript { pub property_a: GString, + #[export] pub editor_prop: u16, @@ -22,6 +23,14 @@ struct TestScript { #[signal] changed: Signal<(u8, u8)>, + pub node_prop: Option>, + + #[export(ty = "Decal")] + pub node_prop_2: Option>, + + #[export] + pub node_array: Array>, + base: Gd<::Base>, }