diff --git a/godot-core/src/meta/class_id.rs b/godot-core/src/meta/class_id.rs index 5976c4882..51fea48ef 100644 --- a/godot-core/src/meta/class_id.rs +++ b/godot-core/src/meta/class_id.rs @@ -67,7 +67,8 @@ impl ClassId { /// We discourage calling this function from different places for the same `T`. But if you do so, `init_fn` must return the same string. /// /// # Panics - /// If the string is not ASCII and the Godot version is older than 4.4. From Godot 4.4 onwards, class names can be Unicode. + /// If the string contains non-ASCII characters and the Godot version is older than 4.4. From Godot 4.4 onwards, class names can be Unicode; + /// See . pub fn new_cached(init_fn: impl FnOnce() -> String) -> Self { Self::new_cached_inner::(init_fn) } @@ -94,14 +95,29 @@ impl ClassId { cache.insert_class_id(Cow::Owned(name), Some(type_id), false) } - /// Create a `ClassId` from a runtime string (for dynamic class names). + /// Create a `ClassId` from a class name only known at runtime. /// - /// Will reuse existing `ClassId` entries if the string is recognized. - // Deliberately not public. - pub(crate) fn new_dynamic(class_name: String) -> Self { + /// Unlike [`ClassId::new_cached()`], this doesn't require a static type parameter. Useful for classes defined outside Rust code, e.g. in + /// scripts. + /// + /// Multiple calls with the same name return equal `ClassId` instances (but may need a lookup). + /// + /// # Example + /// ```no_run + /// use godot::meta::ClassId; + /// + /// let a = ClassId::new_dynamic("MyGDScriptClass"); + /// let b = ClassId::new_dynamic("MyGDScriptClass"); + /// assert_eq!(a, b); + /// ``` + /// + /// # Panics + /// If the string contains non-ASCII characters and the Godot version is older than 4.4. From Godot 4.4 onwards, class names can be Unicode; + /// See . + pub fn new_dynamic(class_name: impl Into) -> Self { let mut cache = CLASS_ID_CACHE.lock(); - cache.insert_class_id(Cow::Owned(class_name), None, false) + cache.insert_class_id(class_name.into(), None, false) } // Test-only APIs. @@ -117,7 +133,10 @@ impl ClassId { Self::new_dynamic(class_name.to_string()) } - #[doc(hidden)] + /// Returns a `ClassId` representing "no class" (empty class name) for non-object property types. + /// + /// This is used for properties that don't have an associated class, e.g. built-in types like `i32`, `GString`, `Vector3` etc. + /// When constructing a [`PropertyInfo`](crate::meta::PropertyInfo) for non-class types, you can use `ClassId::none()` for the `class_id` field. pub fn none() -> Self { // First element is always the empty string name. Self { global_index: 0 } diff --git a/godot-core/src/meta/method_info.rs b/godot-core/src/meta/method_info.rs index 64856cb0a..b350cf61d 100644 --- a/godot-core/src/meta/method_info.rs +++ b/godot-core/src/meta/method_info.rs @@ -12,21 +12,95 @@ use crate::global::MethodFlags; use crate::meta::{ClassId, PropertyInfo}; use crate::sys; -/// Describes a method in Godot. +/// Describes a method's signature and metadata required by the Godot engine. /// -/// Abstraction of the low-level `sys::GDExtensionMethodInfo`. -// Currently used for ScriptInstance. -// TODO check overlap with (private) ClassMethodInfo. +/// Primarily used when implementing custom script instances via the [`ScriptInstance`][crate::obj::script::ScriptInstance] trait. +/// It contains metadata Godot needs to describe and call a method. +/// +/// `MethodInfo` is a high-level abstraction over the low-level FFI type `sys::GDExtensionMethodInfo`. +/// +/// See also [`PropertyInfo`] for describing individual method parameters and return types. +/// +/// # Example +/// ```no_run +/// use godot::meta::{MethodInfo, PropertyInfo, PropertyHintInfo, ClassId}; +/// use godot::builtin::{StringName, Variant, VariantType}; +/// use godot::global::{MethodFlags, PropertyUsageFlags}; +/// use godot::classes::Node2D; +/// use godot::obj::GodotClass; // Trait method ::class_id(). +/// +/// // Describe a Godot method (`World` is a GDScript class): +/// // func spawn_at(world: World, position: Vector2) -> Node2D. +/// let method = MethodInfo { +/// id: 0, +/// method_name: StringName::from("spawn_at"), +/// class_name: ClassId::none(), +/// return_type: PropertyInfo { +/// variant_type: VariantType::OBJECT, +/// class_id: Node2D::class_id(), +/// property_name: StringName::default(), // Return types use empty string. +/// hint_info: PropertyHintInfo::none(), +/// usage: PropertyUsageFlags::DEFAULT, +/// }, +/// arguments: vec![ +/// PropertyInfo { +/// variant_type: VariantType::OBJECT, +/// class_id: ClassId::new_dynamic("World"), +/// property_name: StringName::from("world"), +/// hint_info: PropertyHintInfo::none(), +/// usage: PropertyUsageFlags::DEFAULT, +/// }, +/// PropertyInfo { +/// variant_type: VariantType::VECTOR2, +/// class_id: ClassId::none(), +/// property_name: StringName::from("position"), +/// hint_info: PropertyHintInfo::none(), +/// usage: PropertyUsageFlags::DEFAULT, +/// }, +/// ], +/// default_arguments: vec![], +/// flags: MethodFlags::DEFAULT, +/// }; +/// ``` #[derive(Clone, Debug)] pub struct MethodInfo { + /// Unique identifier for the method within its class. + /// + /// This ID can be used to distinguish between methods and is typically set by the implementation. For script instances, + /// this is often just a sequential index. pub id: i32, + + /// The name of the method, as it appears in Godot. pub method_name: StringName, + + /// The class this method belongs to. + /// + /// For script-defined methods, this is typically the script's class ID obtained via [`ClassId::new_dynamic()`]. + /// Use [`ClassId::none()`] if the class is not applicable or unknown. pub class_name: ClassId, + + /// Description of the method's return type. + /// + /// See [`PropertyInfo`] for how to construct type information. For methods that + /// don't return a value (void), use `VariantType::NIL`. pub return_type: PropertyInfo, + + /// Descriptions of each method parameter. + /// + /// Each element describes one parameter's type, name, and metadata. The order + /// matches the parameter order in the method signature. pub arguments: Vec, - /// Whether default arguments are real "arguments" is controversial. From the function PoV they are, but for the caller, - /// they are just pre-set values to fill in for missing arguments. + + /// Default values for parameters with defaults. + /// + /// Contains the actual default [`Variant`] values for parameters that have them. + /// The length of this vector is typically less than or equal to `arguments.len()`, + /// containing defaults only for trailing parameters. pub default_arguments: Vec, + + /// Method flags controlling behavior and access. + /// + /// See [`MethodFlags`] for available options like `NORMAL`, `VIRTUAL`, `CONST`, etc. pub flags: MethodFlags, } @@ -35,6 +109,7 @@ impl MethodInfo { /// [`free_owned_method_sys`](Self::free_owned_method_sys). /// /// This will leak memory unless used together with `free_owned_method_sys`. + #[doc(hidden)] pub fn into_owned_method_sys(self) -> sys::GDExtensionMethodInfo { use crate::obj::EngineBitfield as _; @@ -88,6 +163,7 @@ impl MethodInfo { /// /// * Must only be used on a struct returned from a call to `into_owned_method_sys`, without modification. /// * Must not be called more than once on a `sys::GDExtensionMethodInfo` struct. + #[doc(hidden)] #[deny(unsafe_op_in_unsafe_fn)] pub unsafe fn free_owned_method_sys(info: sys::GDExtensionMethodInfo) { // Destructure info to ensure all fields are used. diff --git a/godot-core/src/meta/property_info.rs b/godot-core/src/meta/property_info.rs index 339a155d8..5c7156601 100644 --- a/godot-core/src/meta/property_info.rs +++ b/godot-core/src/meta/property_info.rs @@ -15,11 +15,40 @@ use crate::registry::class::get_dyn_property_hint_string; use crate::registry::property::{Export, Var}; use crate::{classes, sys}; -/// Describes a property in Godot. +/// Describes a property's type, name and metadata for Godot. /// -/// Abstraction of the low-level `sys::GDExtensionPropertyInfo`. +/// `PropertyInfo` is used throughout the Godot binding to describe properties, method parameters and return types. /// -/// Keeps the actual allocated values (the `sys` equivalent only keeps pointers, which fall out of scope). +/// This is a high-level abstraction over the low-level FFI type `sys::GDExtensionPropertyInfo`. Unlike the FFI version which only stores +/// pointers, `PropertyInfo` owns its data, ensuring it remains valid for the lifetime of the struct. +/// +/// See also [`MethodInfo`](crate::meta::MethodInfo) for describing method signatures and [`ClassId`] for type-IDs of Godot classes. +/// +/// # Construction +/// For most use cases, prefer the convenience constructors: +/// - [`new_var::()`](Self::new_var) -- creates property info for a `#[var]` attribute. +/// - [`new_export::()`](Self::new_export) -- for an `#[export]` attribute. +/// - [`new_group()`](Self::new_group) / [`new_subgroup()`](Self::new_subgroup) -- for editor groups. +/// +/// # Example +/// ```no_run +/// use godot::meta::{PropertyInfo, PropertyHintInfo, ClassId}; +/// use godot::builtin::{StringName, VariantType}; +/// use godot::global::PropertyUsageFlags; +/// +/// // Integer property without a specific class +/// let count_property = PropertyInfo { +/// variant_type: VariantType::INT, +/// class_id: ClassId::none(), // Only OBJECT types need a real class ID. +/// property_name: StringName::from("count"), +/// hint_info: PropertyHintInfo::none(), +/// usage: PropertyUsageFlags::DEFAULT, +/// }; +/// ``` +/// +/// Here, `class_id` is set to [`ClassId::none()`] because integer properties do not require a specific class. For objects, you can use +/// [`ClassId::new_cached::(...)`][ClassId::new_cached] if you have a Rust type, or [`ClassId::new_dynamic(...)`][ClassId::new_dynamic] +/// for dynamic contexts and script classes. #[derive(Clone, Debug)] // Note: is not #[non_exhaustive], so adding fields is a breaking change. Mostly used internally at the moment though. // Note: There was an idea of a high-level representation of the following, but it's likely easier and more efficient to use introspection @@ -30,46 +59,65 @@ use crate::{classes, sys}; // Object { class_id: ClassId }, // } pub struct PropertyInfo { - /// Which type this property has. + /// Type of the property. /// - /// For objects this should be set to [`VariantType::OBJECT`], and the `class_name` field to the actual name of the class. - /// - /// For [`Variant`][crate::builtin::Variant], this should be set to [`VariantType::NIL`]. + /// For objects, this should be set to [`VariantType::OBJECT`] and use the `class_id` field to specify the actual class. \ + /// For generic [`Variant`](crate::builtin::Variant) properties, use [`VariantType::NIL`]. pub variant_type: VariantType, - /// Which class this property is. + /// The specific class identifier for object-typed properties in Godot. + /// + /// This is only relevant when `variant_type` is set to [`VariantType::OBJECT`]. + /// + /// # Example + /// ```no_run + /// use godot::meta::ClassId; + /// use godot::classes::Node3D; + /// use godot::obj::GodotClass; // Trait method ::class_id(). /// - /// This should be set to [`ClassId::none()`] unless the variant type is `Object`. You can use - /// [`GodotClass::class_id()`] to get the right name to use here. + /// let none_id = ClassId::none(); // For built-ins (not classes). + /// let static_id = Node3D::class_id(); // For classes with a Rust type. + /// let dynamic_id = ClassId::new_dynamic("MyScript"); // For runtime class names. + /// ``` pub class_id: ClassId, - /// The name of this property in Godot. + /// The name of this property as it appears in Godot's object system. pub property_name: StringName, - /// Additional type information for this property, e.g. about array types or enum values. Split into `hint` and `hint_string` members. + /// Additional type information and validation constraints for this property. + /// + /// Use functions from [`export_info_functions`](crate::registry::property::export_info_functions) to create common hints, + /// or [`PropertyHintInfo::none()`] for no hints. /// - /// See also [`PropertyHint`] in the Godot docs. + /// See [`PropertyHintInfo`] struct in Rust, as well as [`PropertyHint`] in the official Godot documentation. /// /// [`PropertyHint`]: https://docs.godotengine.org/en/latest/classes/class_%40globalscope.html#enum-globalscope-propertyhint pub hint_info: PropertyHintInfo, - /// How this property should be used. See [`PropertyUsageFlags`] in Godot for the meaning. + /// Flags controlling how this property should be used and displayed by the Godot engine. + /// + /// Common values: + /// - [`PropertyUsageFlags::DEFAULT`] -- standard property (readable, writable, saved, appears in editor). + /// - [`PropertyUsageFlags::STORAGE`] -- persisted, but not shown in editor. + /// - [`PropertyUsageFlags::EDITOR`] -- shown in editor, but not persisted. + /// + /// See also [`PropertyUsageFlags`] in the official Godot documentation for a complete list of flags. /// /// [`PropertyUsageFlags`]: https://docs.godotengine.org/en/latest/classes/class_%40globalscope.html#enum-globalscope-propertyusageflags pub usage: PropertyUsageFlags, } impl PropertyInfo { - /// Create a new `PropertyInfo` representing a property named `property_name` with type `T`. + /// Create a new `PropertyInfo` representing a property named `property_name` with type `T` automatically. /// - /// This will generate property info equivalent to what a `#[var]` attribute would. + /// This will generate property info equivalent to what a `#[var]` attribute would produce. pub fn new_var(property_name: &str) -> Self { T::Via::property_info(property_name).with_hint_info(T::var_hint()) } - /// Create a new `PropertyInfo` representing an exported property named `property_name` with type `T`. + /// Create a new `PropertyInfo` for an exported property named `property_name` with type `T` automatically. /// - /// This will generate property info equivalent to what an `#[export]` attribute would. + /// This will generate property info equivalent to what an `#[export]` attribute would produce. pub fn new_export(property_name: &str) -> Self { T::Via::property_info(property_name).with_hint_info(T::export_hint()) } @@ -79,8 +127,7 @@ impl PropertyInfo { /// See [`export_info_functions`](crate::registry::property::export_info_functions) for functions that return appropriate `PropertyHintInfo`s for /// various Godot annotations. /// - /// # Examples - /// + /// # Example /// Creating an `@export_range` property. /// // TODO: Make this nicer to use. @@ -156,6 +203,7 @@ impl PropertyInfo { // FFI conversion functions /// Converts to the FFI type. Keep this object allocated while using that! + #[doc(hidden)] pub fn property_sys(&self) -> sys::GDExtensionPropertyInfo { use crate::obj::{EngineBitfield as _, EngineEnum as _}; @@ -169,6 +217,7 @@ impl PropertyInfo { } } + #[doc(hidden)] pub fn empty_sys() -> sys::GDExtensionPropertyInfo { use crate::obj::{EngineBitfield as _, EngineEnum as _}; @@ -265,6 +314,13 @@ impl PropertyInfo { // ---------------------------------------------------------------------------------------------------------------------------------------------- /// Info needed by Godot, for how to export a type to the editor. +/// +/// Property hints provide extra metadata about the property, such as: +/// - Range constraints for numeric values. +/// - Enum value lists. +/// - File/directory paths. +/// - Resource types. +/// - Array element types. #[derive(Clone, Eq, PartialEq, Debug)] pub struct PropertyHintInfo { pub hint: PropertyHint, diff --git a/itest/rust/src/object_tests/class_name_test.rs b/itest/rust/src/object_tests/class_id_test.rs similarity index 93% rename from itest/rust/src/object_tests/class_name_test.rs rename to itest/rust/src/object_tests/class_id_test.rs index ccaf0db68..c6660bbf6 100644 --- a/itest/rust/src/object_tests/class_name_test.rs +++ b/itest/rust/src/object_tests/class_id_test.rs @@ -172,3 +172,17 @@ fn class_name_alloc_panic() { }); } } + +#[itest] +fn class_id_none() { + let none = ClassId::none(); + assert_eq!(none.to_string(), ""); + assert_eq!(none, ClassId::none()); +} + +#[itest] +fn class_id_new_dynamic() { + let class_id = ClassId::new_dynamic("Someクラス名"); + assert_eq!(class_id.to_string(), "Someクラス名"); + assert_eq!(class_id, ClassId::new_dynamic("Someクラス名")); +} diff --git a/itest/rust/src/object_tests/mod.rs b/itest/rust/src/object_tests/mod.rs index 319d8a8dd..bcb216f7f 100644 --- a/itest/rust/src/object_tests/mod.rs +++ b/itest/rust/src/object_tests/mod.rs @@ -7,7 +7,7 @@ mod base_test; mod call_deferred_test; -mod class_name_test; +mod class_id_test; mod class_rename_test; mod dyn_gd_test; mod dynamic_call_test;