diff --git a/Cargo.toml b/Cargo.toml index 3cfbebd..5fe1080 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,6 @@ thiserror = "1" godot-rust-script-derive = { path = "derive" } tests-scripts-lib = { path = "tests-scripts-lib" } + +[workspace.lints.rust] +missing_docs = "warn" diff --git a/README.md b/README.md index 11426a7..edc0471 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ For this, a manual implementation of the `godot::init::ExtensionLibrary` trait i be achieved via two macro calls. The `init!(...)` macro requires the name / path to a module in your library, which represents the root module of all available scripts. -```rs +```rust struct Lib; #[gdextension] @@ -75,7 +75,7 @@ unsafe impl ExtensionLibrary for Lib { Rust scripts require a root module. All rust modules under this module will be considered as potential scripts. -```rs +```rust mod example_script; godot_rust_script::define_script_root!(); @@ -92,7 +92,7 @@ Scripts are then composed of a `struct` definition and an `impl` block. Public f other scripting languages and the engine, so they must use Godot compatible types. The same applies to struct fields. Struct fields can additionally be exported via the `#[export]` attribute, so they show up in the editor inspector. -```rs +```rust use godot_rust_script::{ godot::prelude::{godot_print, Gd, GodotString, Node3D, Object}, godot_script_impl, GodotScript, diff --git a/derive/Cargo.toml b/derive/Cargo.toml index b86b192..1c6ee93 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -12,3 +12,6 @@ proc-macro2.workspace = true quote.workspace = true syn.workspace = true itertools.workspace = true + +[lints] +workspace = true diff --git a/derive/src/lib.rs b/derive/src/lib.rs index c4a60cb..3b722fe 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -3,6 +3,7 @@ * 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/. */ +#![doc = "Derive macros for the godot-rust-script crate."] mod attribute_ops; mod enums; @@ -19,6 +20,60 @@ use type_paths::{godot_types, property_hints, string_name_ty, variant_ty}; use crate::attribute_ops::{FieldExportOps, FieldSignalOps, PropertyOpts}; +/// Use this derive macro to create new rust scripts for your projects. +/// +/// The macro is desinged to closely align with both godot-rusts [`GodotClass`](https://docs.rs/godot/latest/godot/prelude/derive.GodotClass.html) macro and the GDScript +/// annotations. +/// +/// # Top Level Attribute +/// On the struct level the `#[script]` attribute can be used to configure base details of the script. +/// +/// ## `#[script(base)]` +/// ``` +/// # use ::godot_rust_script::{GodotScript, godot_script_impl}; +/// # use ::godot::classes::Node3D; +/// # +/// #[derive(GodotScript, Debug)] +/// #[script(base = Node3D)] +/// struct MyScript {} +/// +/// # #[godot_script_impl] +/// # impl MyScript {} +/// ``` +/// +/// Set the `base` field to specify a base class your script should inherit from. By default all scripts inherit from [`RefCounted`](https://docs.rs/godot/latest/godot/classes/struct.RefCounted.html). +/// +/// # Field Level Attributes +/// On the field level you can specify customizations for your script properties. Fields that are private will not be exposed to the engine. +/// Public field on the other hand are exposed to the engine and can be annotated with attributes. +/// +/// ## `#[prop]` +/// Use the `#[prop]` attribute to set up getter and setter functions for your properties. +/// +/// ``` +/// # use godot_rust_script::{GodotScript, godot_script_impl}; +/// # use godot::builtin::GString; +/// # +/// #[derive(GodotScript, Debug)] +/// struct MyScript { +/// #[prop(set = Self::set_my_prop, get = Self::get_my_prop)] +/// my_prop: GString, +/// } +/// +/// #[godot_script_impl] +/// impl MyScript { +/// fn set_my_prop(&mut self, value: GString) { +/// self.my_prop = value; +/// } +/// +/// fn get_my_prop(&self) -> GString { +/// self.my_prop.clone() +/// } +/// } +/// ``` +/// +/// This attribute optionally accepts a `get` and a `set` field. If these fields are defined they have to be set to a function pointer +/// expression. The expression can contain the `Self` keyword. #[proc_macro_derive(GodotScript, attributes(export, script, prop, signal))] pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); @@ -502,6 +557,7 @@ fn derive_signal_metadata(field: &SpannedValue) -> (TokenStream, Toke (metadata, const_assert.unwrap_or_default()) } +/// Attribute for `GodotScript` impls to automatically implement the corret traits. #[proc_macro_attribute] pub fn godot_script_impl( args: proc_macro::TokenStream, @@ -544,6 +600,7 @@ fn extract_ident_from_type(impl_target: &syn::Type) -> Result proc_macro::TokenStream { enums::script_enum_derive(input) diff --git a/rust-script/Cargo.toml b/rust-script/Cargo.toml index d0d9d62..4c80b4d 100644 --- a/rust-script/Cargo.toml +++ b/rust-script/Cargo.toml @@ -28,3 +28,6 @@ godot-bindings.workspace = true default = ["runtime", "scripts"] runtime = ["dep:itertools", "dep:rand"] scripts = ["dep:godot-rust-script-derive"] + +[lints] +workspace = true diff --git a/rust-script/build.rs b/rust-script/build.rs index 51d03ac..461f3ee 100644 --- a/rust-script/build.rs +++ b/rust-script/build.rs @@ -3,6 +3,7 @@ * 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/. */ +#![doc = "Build script for the godot-rust-script crate."] fn main() { godot_bindings::emit_godot_version_cfg(); diff --git a/rust-script/src/interface.rs b/rust-script/src/interface.rs index 48829ac..59b62ba 100644 --- a/rust-script/src/interface.rs +++ b/rust-script/src/interface.rs @@ -23,13 +23,32 @@ pub use on_editor::OnEditor; #[expect(deprecated)] pub use signals::{ScriptSignal, Signal}; +/// The primary trait of this library. This trait must be implemented by a struct to create a new rust script. +/// +/// While it is possible, it's not intended that this trait is implemented by hand. Use the [derive macro](derive@crate::GodotScript) to +/// implement this trait. pub trait GodotScript: Debug + GodotScriptImpl { + /// The base godot class of the script. + /// + /// It's currently not possible to use an other script as the base. type Base: Inherits; + /// The globally unique class name of the rust script. const CLASS_NAME: &'static str; + /// Set the value of a script property as a [`Variant`]. + /// + /// This is called by the engine to interact with the script. fn set(&mut self, name: StringName, value: Variant) -> bool; + + /// Get the value of a script property as a [`Variant`]. + /// + /// This is called by the engine to interact with the script. fn get(&self, name: StringName) -> Option; + + /// Call a script method with arguments. + /// + /// The engine will use this to pass method calls from other scripts and engine callbacks. fn call( &mut self, method: StringName, @@ -37,15 +56,30 @@ pub trait GodotScript: Debug + GodotScriptImpl { context: Context<'_, Self>, ) -> Result; + /// String representation of the script. + /// + /// This is mostly used in debug formatting. fn to_string(&self) -> String; fn property_state(&self) -> HashMap; + /// Create the default state of the script. + /// + /// The base is passed no matter if it's required or not. fn default_with_base(base: godot::prelude::Gd) -> Self; } +/// Inditection to dispatch script method calls. +/// +/// To support implementing method dispatch via macros the actual call dispatch logic is split into a separate trait. pub trait GodotScriptImpl { + /// The godot base class that is expected for the function call dispatch. + /// + /// This has to match the base of the [`GodotScript`] impl. type ImplBase: Inherits; + /// Dispatch calls to script methods. + /// + /// Handles the dynamic dispatching of script method calls. Should be called by [`GodotScript::call`]. fn call_fn( &mut self, name: StringName, @@ -151,24 +185,50 @@ where } } +/// Script downcasting error +/// +/// This error can occour when trying to downcast an object into a specifc script. If the desired script is actually attached to the object +/// has to be verified at runtime. #[derive(thiserror::Error, Debug)] pub enum GodotScriptCastError { + /// Occours when an object doesn't have a script attached at runtime. #[error("Object has no script attached!")] NoScriptAttached, + /// Occours when the attached script is not a `RustScript`. #[error("Script attached to object is not a RustScript!")] NotRustScript, + /// Occours when the attached `RustScript` class does not match at runtime. #[error( "Script attached to object does not match expected script class `{0}` but found `{1}`!" )] ClassMismatch(&'static str, String), } +/// This trait allows casting engine objects / types into a [`RsRef`]. pub trait CastToScript { + /// Falibly Cast the object into a rust script reference. + /// + /// The error can be handled if the conversion fails. fn try_to_script(&self) -> Result, GodotScriptCastError>; + + /// Falibly cast the object into a rust script reference without incrementing the ref-count. + /// + /// This is a bit more efficient than [`try_to_script`]. + /// The error can be handled if the conversion fails. fn try_into_script(self) -> Result, GodotScriptCastError>; + + /// Cast the object into a rust script reference. + /// + /// # Panics + /// - if the expected script is not attached to the object at runtime. fn to_script(&self) -> RsRef; + + /// Cast the object into a rust script reference without incrementing the ref-count. + /// + /// # Panics + /// - if the expected script is not attached to the object at runtime. fn into_script(self) -> RsRef; } @@ -218,6 +278,7 @@ impl + Inherits> CastToScript fo /// property from the engine. This Trait separates the `::godot::prelude::Var` trait into it's get and set components for more granular /// requirements on the property types. pub trait GetScriptProperty: GodotConvert { + /// Get the value of a script property as it's godot engine type. fn get_property(&self) -> Self::Via; } @@ -227,6 +288,7 @@ pub trait GetScriptProperty: GodotConvert { /// property from the engine. This Trait separates the `::godot::prelude::Var` trait into it's get and set components for more granular /// requirements on the property types. pub trait SetScriptProperty: GodotConvert { + /// Set the value of a script property as it's godot engine type. fn set_property(&mut self, value: Self::Via); } @@ -235,6 +297,7 @@ pub trait SetScriptProperty: GodotConvert { /// Most of the time we can initialize a script property with the `Default` trait. To support cases where `Default` is not implemented we /// can manually implement this trait. pub trait InitScriptProperty { + /// Initialize the default value of a script property. fn init_property() -> Self; } @@ -265,10 +328,26 @@ where } } +/// Defines the root module for rust scripts. All scripts must be in submodules of the root module. +/// +/// There must be a script root module in your project for Godot Rust Script to work. Using multiple root modules is currently not supported. +/// +/// # Example +/// ```ignore +/// # use godot_rust_script::define_script_root; +/// // Example script root: src/scripts/mod.rs +/// +/// // define your script modules that contain `RustScript` structs. +/// mod player; +/// mod mob; +/// +/// define_script_root!(); +/// ``` #[macro_export] macro_rules! define_script_root { () => { #[no_mangle] + #[doc(hidden)] pub fn __godot_rust_script_init( ) -> ::std::vec::Vec<$crate::private_export::RustScriptMetaData> { use $crate::godot::obj::EngineEnum; @@ -281,6 +360,7 @@ macro_rules! define_script_root { $crate::private_export::assemble_metadata(lock.iter()) } + #[doc(hidden)] pub const __GODOT_RUST_SCRIPT_SRC_ROOT: &str = $crate::private_export::concat!( env!("CARGO_MANIFEST_DIR"), "/src", @@ -296,6 +376,7 @@ macro_rules! define_script_root { }; } +/// DEPRECATED: This macro has been renamed to [define_script_root]. #[deprecated = "Has been renamed to define_script_root!()"] #[macro_export] macro_rules! setup_library { @@ -306,6 +387,43 @@ macro_rules! setup_library { pub trait GodotScriptEnum: GodotConvert + FromGodot + ToGodot {} +/// Initialize the rust script runtime. This should be part of your `ExtensionLibrary::on_level_init` function. +/// +/// # Example +/// ``` +/// # use godot::init::{gdextension, InitLevel, ExtensionLibrary}; +/// # +/// # mod scripts { +/// # pub const __GODOT_RUST_SCRIPT_SRC_ROOT: &str = "/dummy/root"; +/// # +/// # pub fn __godot_rust_script_init() -> Vec { +/// # unimplemented!() +/// # } +/// # } +/// # +/// struct Lib; +/// +/// #[gdextension] +/// unsafe impl ExtensionLibrary for Lib { +/// fn on_level_init(level: InitLevel) { +/// match level { +/// InitLevel::Core => (), +/// InitLevel::Servers => (), +/// InitLevel::Scene => godot_rust_script::init!(scripts), +/// InitLevel::Editor => (), +/// } +/// } +/// +/// # fn on_level_deinit(level: InitLevel) { +/// # match level { +/// # InitLevel::Editor => (), +/// # InitLevel::Scene => godot_rust_script::deinit!(), +/// # InitLevel::Servers => (), +/// # InitLevel::Core => (), +/// # } +/// # } +/// } +/// ```` #[macro_export] macro_rules! init { ($scripts_module:tt) => { @@ -316,6 +434,43 @@ macro_rules! init { }; } +/// Deinitialize the rust script runtime. This should be part of your `ExtensionLibrary::on_level_deinit` function. +/// +/// # Example +/// ``` +/// # use godot::init::{gdextension, InitLevel, ExtensionLibrary}; +/// # +/// # mod scripts { +/// # pub const __GODOT_RUST_SCRIPT_SRC_ROOT: &str = "/dummy/root"; +/// # +/// # pub fn __godot_rust_script_init() -> Vec { +/// # unimplemented!() +/// # } +/// # } +/// # +/// struct Lib; +/// +/// #[gdextension] +/// unsafe impl ExtensionLibrary for Lib { +/// # fn on_level_init(level: InitLevel) { +/// # match level { +/// # InitLevel::Core => (), +/// # InitLevel::Servers => (), +/// # InitLevel::Scene => godot_rust_script::init!(scripts), +/// # InitLevel::Editor => (), +/// # } +/// # } +/// # +/// fn on_level_deinit(level: InitLevel) { +/// match level { +/// InitLevel::Editor => (), +/// InitLevel::Scene => godot_rust_script::deinit!(), +/// InitLevel::Servers => (), +/// InitLevel::Core => (), +/// } +/// } +/// } +/// ```` #[macro_export] macro_rules! deinit { () => { diff --git a/rust-script/src/interface/export.rs b/rust-script/src/interface/export.rs index 51755e5..650977d 100644 --- a/rust-script/src/interface/export.rs +++ b/rust-script/src/interface/export.rs @@ -23,6 +23,9 @@ use godot::sys::GodotFfi; use super::{GodotScript, OnEditor, RsRef}; +/// Represents a type that can be used for an exported script property. +/// +/// Types that implement this trait can be used for exported properties. The trait provides the necessary meta-data to the engine. pub trait GodotScriptExport { fn hint_string(custom_hint: Option, custom_string: Option) -> String; diff --git a/rust-script/src/lib.rs b/rust-script/src/lib.rs index a868c34..f1fecfa 100644 --- a/rust-script/src/lib.rs +++ b/rust-script/src/lib.rs @@ -4,6 +4,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#![doc = include_str!("../../README.md")] + mod apply; mod editor_ui_hacks; @@ -11,7 +13,8 @@ mod interface; mod runtime; mod static_script_registry; -pub use godot_rust_script_derive::{godot_script_impl, GodotScript, GodotScriptEnum}; +pub use godot_rust_script_derive::GodotScript; +pub use godot_rust_script_derive::{godot_script_impl, GodotScriptEnum}; pub use interface::*; pub use runtime::RustScriptExtensionLayer; diff --git a/rust-script/src/static_script_registry.rs b/rust-script/src/static_script_registry.rs index b798ddc..a0f9db4 100644 --- a/rust-script/src/static_script_registry.rs +++ b/rust-script/src/static_script_registry.rs @@ -21,6 +21,7 @@ use crate::runtime::GodotScriptObject; godot::sys::plugin_registry!(pub SCRIPT_REGISTRY: RegistryItem); +#[doc(hidden)] #[macro_export] macro_rules! register_script_class { ($class_name:ty, $base_name:ty, $desc:expr, $props:expr, $signals:expr) => { @@ -43,6 +44,7 @@ macro_rules! register_script_class { }; } +#[doc(hidden)] #[macro_export] macro_rules! register_script_methods { ($class_name:ty, $methods:expr) => { diff --git a/tests-scripts-lib/Cargo.toml b/tests-scripts-lib/Cargo.toml index 37403b4..108aa82 100644 --- a/tests-scripts-lib/Cargo.toml +++ b/tests-scripts-lib/Cargo.toml @@ -9,3 +9,5 @@ crate-type = ["dylib", "rlib"] [dependencies] godot-rust-script = { path = "../rust-script", features = ["scripts"] } +[lints] +workspace = true diff --git a/tests-scripts-lib/src/lib.rs b/tests-scripts-lib/src/lib.rs index 2878321..8deb1e6 100644 --- a/tests-scripts-lib/src/lib.rs +++ b/tests-scripts-lib/src/lib.rs @@ -3,5 +3,6 @@ * 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/. */ +#![allow(missing_docs)] godot_rust_script::define_script_root!();