# gdrust_export_node_path_macro Reduces boilerplate code when acquiring references through `NodePath`. Usage consists of (full example on a few paragraphs bellow): - Replacing `#[derive(NativeClass, Default)] #[inherit(Node)]` with `#[extends(Node)]` - Removing the `NodePath` fields marked with `#[property]` - Placing `#[export_path]` behind the fields you wish to acquire through `NodePath` - Removing your implementation of `fn new(&mut self, _owner: &Node)` - Manually call `self.grab_nodes_by_path(_owner);` on your `_ready()` declaration ### The `#[extends]` attribute macro: - Replaces itself with `#[derive(NativeClass)] #[inherit(Node)]` - Then, for each field marked with `#[export_path]`: - Declares another field of similar name but with a `path_` prefix and `NodePath` as type, as well as a regular `#[property]` attribute. (`Vec>` uses a `Vec` type instead) - Example input/output: `#[export_path] node: Option>` / `#[property] path_node: NodePath, node: Option>,` - Declares a impl block with two functions: - `fn grab_nodes_by_path(&mut self, _owner: &Node) { // searches for all the nodes/instances from the NodePath fields generated, assigning each to their original field in self }` - `fn new(_owner: &Node) -> Self { Self::default() }` ### Supports exporting: - `Option>` where T is a Godot's built-in type that inherits `Node` (such as: `Node2D`, `Control`, `ProgressBar`, `KinematicBody`, ...) - `Vec>` where T is a Godot's built-in type that inherits `Node` - `Option>` where T is a custom native script defined by you, as long as the script inherits a Godot's built-in type that inherits `Node` - `Vec>` where T is a custom native script defined by you, as long as the script inherits a Godot's built-in type that inherits `Node` PS 1: Note that `Vec>`/`Vec>` uses `Ref`/`Instance` directly instead of `Option>`/`Option>` PS 2: Not related to this crate, but to `gdnative-rust` itself: In the editor, `Vec` will be shown as an array of `Variant`, you have to click on each element of the array, chose `NodePath`, then you'll be able to drag nodes in. The base of the implementation was taken from [gdrust](https://github.com/wyattjsmith1/gdrust). ## Usage ### Replace ```rs #[derive(NativeClass)] #[inherit(Node)] struct MyGodotScript { #[property] path_exported_node: NodePath, exported_node: Option>, #[property] path_exported_instance: NodePath, exported_instance: Option>, #[property] paths_exported_nodes: Vec, vec_nodes: Vec>, #[property] paths_exported_instances: Vec, vec_instances: Vec>, } impl MyGodotScript { fn new(_owner: &Node) -> Self { return Self { path_exported_node: NodePath::default(), exported_node: None, path_exported_instance: NodePath::default(), exported_instance: None, paths_exported_nodes: Vec::new(), vec_nodes: Vec::new(), paths_exported_instances: Vec::new(), vec_instances: Vec::new(), }; } } #[methods] impl MyGodotScript { #[method] fn _ready(&mut self, #[base] _owner: &Node) { self.exported_node = Some(unsafe {_owner.get_node_as::(self.path_exported_node.new_ref()).unwrap().assume_shared()}); self.exported_instance = Some(unsafe {_owner.get_node_as_instance::(self.path_exported_instance.new_ref()).unwrap().claim()}); for path in self.paths_exported_nodes.iter() { self.vec_nodes.push(unsafe { _owner.get_node_as::(path.new_ref()).unwrap().assume_shared()}); } for path in self.paths_exported_instances.iter() { self.vec_instances.push(unsafe { _owner.get_node_as_instance::(path.new_ref()).unwrap().claim()}); } } } #[derive(NativeClass)] #[inherit(Node)] pub struct NativeScriptTest { } impl NativeScriptTest { fn new(_owner: &Node) -> Self { return Self {}; } } ``` ### With ```rs #[extends(Node)] // you can replace Node with any other Godot built-in node type struct MyGodotScript { #[export_path] exported_node: Option>, // you can replace Node with any other Godot built-in node type #[export_path] exported_instance: Option>, // replace NativeScriptTest with your own type #[export_path] vec_nodes: Vec>, // you can replace Node with any other Godot built-in node type #[export_path] vec_instances: Vec>, // replace NativeScriptTest with your own type } #[methods] impl MyGodotScript { #[method] fn _ready(&mut self, #[base] _owner: &Node) { // replace Node with the extended type self.grab_nodes_by_path(_owner); // you must call this manually, it replaces your old _ready() call } } #[extends(Node)] pub struct NativeScriptTest { } ``` ### Which expands to (manually formatted for readability) ```rs #[derive(gdnative::prelude::NativeClass, Default)] #[inherit(Node)] struct MyGodotScript { #[property] path_vec_instances: Vec, #[property] path_vec_nodes : Vec, #[property] path_exported_instance: gdnative::prelude::NodePath, #[property] path_exported_node : gdnative::prelude::NodePath, exported_node: Option>, exported_instance: Option>, vec_nodes: Vec>, vec_instances: Vec>, } impl MyGodotScript { fn new(_owner: &Node) -> Self { Self::default() } fn grab_nodes_by_path(&mut self, owner: &Node) { self.exported_node = Some(unsafe { owner.get_node_as::(self.path_exported_node.new_ref()).unwrap().assume_shared() }); self.exported_instance = Some(unsafe { owner.get_node_as_instance::(self.path_exported_instance.new_ref()).unwrap().claim() }); for path in self.path_vec_nodes.iter() { self.vec_nodes.push(unsafe { owner.get_node_as::(path.new_ref()).unwrap().assume_shared() }); } for path in self.path_vec_instances.iter() { self.vec_instances.push(unsafe { owner.get_node_as_instance::(path.new_ref()).unwrap().claim() }); } } } ``` ## Misc / Limitations - You may still freely use the other `gdnative` attributes like `#[register_with]` `#[no_constructor]` `#[user_data]` `#[property]`. Just make sure to always place them bellow `#[extends]`, except `#[property]` which still goes behind fields. - `grab_nodes_by_path(&mut self, owner: &Node)` will panic if it fails to validate any of the exported paths. - You cannot define your own `Self::new()`. - `_owner` in `grab_nodes_by_path(&mut self, owner: &Node)` uses hardcoded `&`, so you cannot declare `owner` in `_ready(&mut self, #[base] _owner: &Node)` as `owner: TRef`.