diff --git a/Cargo.lock b/Cargo.lock index d6daf15..4ae480b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,7 +107,7 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "node_tree" -version = "0.3.0" +version = "0.5.0" dependencies = [ "chrono", "node_tree_derive", @@ -115,8 +115,9 @@ dependencies = [ [[package]] name = "node_tree_derive" -version = "0.2.0" +version = "0.4.0" dependencies = [ + "proc-macro2", "quote", "syn 1.0.109", ] diff --git a/README.md b/README.md index f61b7b8..1a10ffa 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,34 @@ fn main() -> () { } ``` +You may also input a `NodeScene` when initializing a `NodeTree` or adding a child via `add_child`: +```rust +use node_tree::prelude::*; + + +let scene: NodeScene = scene! { + NodeA("Root") { // Nodes must have a constructor named `new()` in order for this to work! + NodeA("1_Node") { + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + } + } + } +}; +``` + Logging is also supported. Here is an example setup with an output of a few warnings and a crash. Note that the crash header/footer are customizable, and that the output is actually colored in a real terminal. ```rust /// Root Node @@ -228,4 +256,4 @@ Goodbye World! (Program Exited) - 👪 The ability to manage nodes with `add_child()` and `remove_child()`. - 📝 Includes a dynamic logging system that is deeply integrated with the node framework. - 🌲 Allows for the direct referencing of the `NodeTree` through a node's `root()` function. -- 📜 TODO: Includes a method to save and handle individual node scenes, such as the handy visual macro `Scene!`. +- 📜 Includes a method to save (TODO) and handle individual node scenes, such as the handy visual macro `scene!`. diff --git a/node_tree_core/Cargo.toml b/node_tree_core/Cargo.toml index 2cc785b..a1e8b61 100644 --- a/node_tree_core/Cargo.toml +++ b/node_tree_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node_tree" -version = "0.4.0" +version = "0.5.0" edition = "2021" rust-version = "1.78" @@ -20,4 +20,4 @@ license = "MIT OR Apache-2.0" [dependencies] chrono = "0.4.38" -node_tree_derive = { path = "../node_tree_derive", version = "0.3.0" } +node_tree_derive = { path = "../node_tree_derive", version = "0.4.0" } diff --git a/node_tree_core/README.md b/node_tree_core/README.md index f61b7b8..1a10ffa 100644 --- a/node_tree_core/README.md +++ b/node_tree_core/README.md @@ -106,6 +106,34 @@ fn main() -> () { } ``` +You may also input a `NodeScene` when initializing a `NodeTree` or adding a child via `add_child`: +```rust +use node_tree::prelude::*; + + +let scene: NodeScene = scene! { + NodeA("Root") { // Nodes must have a constructor named `new()` in order for this to work! + NodeA("1_Node") { + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + } + } + } +}; +``` + Logging is also supported. Here is an example setup with an output of a few warnings and a crash. Note that the crash header/footer are customizable, and that the output is actually colored in a real terminal. ```rust /// Root Node @@ -228,4 +256,4 @@ Goodbye World! (Program Exited) - 👪 The ability to manage nodes with `add_child()` and `remove_child()`. - 📝 Includes a dynamic logging system that is deeply integrated with the node framework. - 🌲 Allows for the direct referencing of the `NodeTree` through a node's `root()` function. -- 📜 TODO: Includes a method to save and handle individual node scenes, such as the handy visual macro `Scene!`. +- 📜 Includes a method to save (TODO) and handle individual node scenes, such as the handy visual macro `scene!`. diff --git a/node_tree_core/src/lib.rs b/node_tree_core/src/lib.rs index 676bb6a..82e2549 100644 --- a/node_tree_core/src/lib.rs +++ b/node_tree_core/src/lib.rs @@ -53,17 +53,19 @@ pub mod prelude { //! You'll probably want to import all from this module. pub use std::rc::Rc; - pub use node_tree_derive::Abstract; + pub use node_tree_derive::{ Abstract, Tree, scene }; pub use crate::structs::{ rid::RID, logger::{ LoggerVerbosity, Log }, node_base::NodeBase, node_path::NodePath, - node_tree_base::{ NodeTreeBase, TreeStatus, TreeProcess, ProcessMode }, - tree_pointer::{ Tp, TpDyn } + node_tree_base::{ NodeTreeBase, TreeStatus, TreeProcess, ProcessMode, initialize_base }, + tree_pointer::{ Tp, TpDyn }, + node_scene::NodeScene }; pub use crate::traits::{ node::{ Node, NodeAbstract }, - node_tree::{ NodeTree, init_base } + node_tree::NodeTree, + instanceable::Instanceable }; } diff --git a/node_tree_core/src/structs/mod.rs b/node_tree_core/src/structs/mod.rs index 55bbb51..8d14ba0 100644 --- a/node_tree_core/src/structs/mod.rs +++ b/node_tree_core/src/structs/mod.rs @@ -4,3 +4,6 @@ pub mod node_path; pub mod node_tree_base; pub mod tree_pointer; pub mod rid; + +#[macro_use] +pub mod node_scene; diff --git a/node_tree_core/src/structs/node_base.rs b/node_tree_core/src/structs/node_base.rs index 452cff7..997d1e8 100644 --- a/node_tree_core/src/structs/node_base.rs +++ b/node_tree_core/src/structs/node_base.rs @@ -35,7 +35,7 @@ use super::{ rid::RID }; -use crate::traits::{ node::Node, node_tree::NodeTree, node_getter::NodeGetter }; +use crate::traits::{ node::Node, node_tree::NodeTree, node_getter::NodeGetter, instanceable::Instanceable }; use crate::utils::functions::ensure_unique_name; @@ -121,18 +121,46 @@ impl NodeBase { /// Adds a child to the node, automatically renaming it if its name is not unique in the /// node's children vector. + /// /// # Note /// `_ready()` will automatically be propogated through the added child node. + /// /// # Panics /// Panics if this Node is not connected to a `NodeTree`. - pub fn add_child(&mut self, mut child: N) { + pub fn add_child(&mut self, child: I) { + child.iterate(|parent, node| { + if let Some(parent) = parent { + unsafe { + let parent: &mut dyn Node = &mut *parent; + parent.add_child_from_ptr(node, false); + } + } else { + unsafe { + self.add_child_from_ptr(node, true); + } + } + }); + } + + /// Adds a child to the node via a passed in pointer, automatically renaming it if its + /// name is not unique in the node's children vector. + /// + /// # Note + /// `_ready()` will automatically be propogated through the added child node. + /// + /// # Safety + /// Cannot guarantee that the raw pointer that is passed in is valid. + /// + /// # Panics + /// Panics if this Node is not connected to a `NodeTree`. + pub unsafe fn add_child_from_ptr(&mut self, child_ptr: *mut dyn Node, owner_is_self: bool) { if self.tree.is_none() { panic!("Cannot add a child to a node that is not in a `NodeTree`!"); } // Ensure that the child's name within the context of this node's children is unique. let names_of_children: &[String] = &self.children().iter().map(|c| c.name().to_string()).collect::>(); - let child_name: &str = child.name(); + let child_name: &str = unsafe { &*child_ptr }.name(); let unique_name: String = ensure_unique_name(&child_name, names_of_children); // Add the child to this node's children and connect it to its parent and owner nodes, @@ -143,17 +171,16 @@ impl NodeBase { let new_depth: usize = self.depth() + 1; let tree_raw: *mut dyn NodeTree = self.tree.unwrap_unchecked(); let tree: &mut dyn NodeTree = self.tree_mut().unwrap_unchecked(); + + let rid: RID = tree.register_node(child_ptr); + let child: &mut dyn Node = tree.get_node_mut(rid).unwrap_unchecked(); child.set_name_unchecked(&unique_name); child.set_parent(parent_rid); - child.set_owner(owner_rid); // For now, we just propagate the root as the owner for all nodes. + child.set_owner(if owner_is_self { rid } else { owner_rid }); child.set_tree(tree_raw); child.set_depth(new_depth); // This is the only place where depth is updated. - let child_ptr: Box = child.to_dyn_box(); - let rid: RID = tree.register_node(child_ptr); - let child: &mut dyn Node = tree.get_node_mut(rid).unwrap_unchecked(); - child.set_rid(rid); rid }; diff --git a/node_tree_core/src/structs/node_scene.rs b/node_tree_core/src/structs/node_scene.rs new file mode 100644 index 0000000..a112eee --- /dev/null +++ b/node_tree_core/src/structs/node_scene.rs @@ -0,0 +1,73 @@ +use crate::traits::{ node::Node, instanceable::Instanceable }; + + +/* + * Node Scene + * Struct + */ + + +/// A recursive structure that allows for the storage of a dormant scene of nodes. +/// The root node is what every node in the scene will have its owner set to. +#[derive(Debug)] +pub struct NodeScene { + this: *mut dyn Node, + children: Vec +} + +impl NodeScene { + + /// Creates a new `NodeScene` with a root node. + pub fn new(root: N) -> Self { + NodeScene { + this: Box::into_raw(root.to_dyn_box()), + children: Vec::new() + } + } + + /// Appends a `NodeScene` as a child. + pub fn append(&mut self, child: NodeScene) { + self.children.push(child); + } + + /// Returns this `NodeScene` instance's associated node. + /// + /// # Safety + /// This is marked unsafe as if the resulting `Box` is dropped, the internal pointer could + /// be invalidated. + pub unsafe fn get_node(&self) -> Box { + Box::from_raw(self.this) + } + + /// Gets the children. + pub fn children(&self) -> &[NodeScene] { + &self.children + } +} + +impl Instanceable for NodeScene { + fn iterate, *mut dyn Node)>(self, mut iterator: F) { + iterator(None, self.this); + + // Recursive function to traverse the tree + fn traverse, *mut dyn Node)>( + node: NodeScene, + parent: *mut dyn Node, + iterator: &mut F + ) { + for child in node.children { + + // Call the iterator for the child node + iterator(Some(parent), child.this); + + // Recursively traverse the child's children + let child_this: *mut dyn Node = child.this; + traverse(child, child_this, iterator); + } + } + + // Start the traversal from the root. + let self_this: *mut dyn Node = self.this; + traverse(self, self_this, &mut iterator); + } +} diff --git a/node_tree_core/src/structs/node_tree_base.rs b/node_tree_core/src/structs/node_tree_base.rs index 920de36..a020455 100644 --- a/node_tree_core/src/structs/node_tree_base.rs +++ b/node_tree_core/src/structs/node_tree_base.rs @@ -52,7 +52,7 @@ use std::collections::{HashMap, HashSet}; use std::time::{ Duration, Instant }; -use crate::traits::{ node::Node, node_tree::NodeTree, node_getter::NodeGetter }; +use crate::traits::{ node::Node, node_tree::NodeTree, node_getter::NodeGetter, instanceable::Instanceable }; use super::logger::*; use super::node_base::NodeStatus; use super::rid::{ RID, RIDHolder }; @@ -164,6 +164,25 @@ impl NodeTreeBase { /// The RID for the root node. const ROOT_RID: RID = 0; + + /// Creates an empty `NodeTreeBase`, ready for initialization. + unsafe fn new(logger_verbosity: LoggerVerbosity) -> Self { + + // Creates a new RID holder which stores all of the Nodes. + let nodes: RIDHolder<*mut dyn Node> = RIDHolder::new(); + + // Create the NodeTreeBase. + let node_tree: NodeTreeBase = NodeTreeBase { + logger: Logger::new(logger_verbosity), + nodes, + identity: HashMap::new(), + singletons: HashMap::new(), + status: TreeStatus::Idle, + last_frame: Instant::now() + }; + + node_tree + } /// Creates a new NodeTreeBase with the pointer to the outer `NodeTree` struct and the given root node. /// @@ -179,40 +198,34 @@ impl NodeTreeBase { /// This is marked as unsafe because it relies on a raw pointer being passed in. /// It is undefined behaviour if the outer struct is not allocated on the heap. /// ... - pub unsafe fn new(outer: *mut dyn NodeTree, root: N, logger_verbosity: LoggerVerbosity) -> Self { - - // Creates a new RID holder which stores all of the Nodes. - let mut nodes: RIDHolder<*mut dyn Node> = RIDHolder::new(); - let _: RID = nodes.push(Box::into_raw(root.to_dyn_box())); // This will ALWAYS be zero! - - // Create the NodeTreeBase. - let mut node_tree: NodeTreeBase = NodeTreeBase { - logger: Logger::new(logger_verbosity), - nodes, - identity: HashMap::new(), - singletons: HashMap::new(), - status: TreeStatus::Idle, - last_frame: Instant::now() - }; - - // Since this is the root node, it's 'owner' will be itself. - // It will also have no parent. - unsafe { - let root: &mut dyn Node = node_tree.root_mut(); - - root.set_rid(Self::ROOT_RID); - root.set_owner(Self::ROOT_RID); - root.set_tree(outer); - } + unsafe fn initialize(&mut self, outer: *mut dyn NodeTree, scene: I) { + + // Go through each node that needs to be instanced in the scene. + scene.iterate(|parent, node| { + if let Some(parent) = parent { + let parent: &mut dyn Node = unsafe { &mut *parent }; + parent.add_child_from_ptr(node, false); + } else { + self.identity.insert(Self::ROOT_RID, NodeIdentity::NodePath); + + // Since this is the root node, it's 'owner' will be itself. + // It will also have no parent. + let root: &mut dyn Node = unsafe { &mut *node }; + unsafe { + root.set_rid(Self::ROOT_RID); + root.set_owner(Self::ROOT_RID); + root.set_tree(outer); + } - let root: &dyn Node = node_tree.root(); - node_tree.logger.post_manual( - SystemCall::Named("NodeTree".to_string()), - Log::Debug(&format!( - "Node \"{}\" added to the scene as the root of the NodeTree! Unique ID of \"{}\" generated!", - root.name(), root.rid() - ))); - node_tree + self.logger.post_manual( + SystemCall::Named("NodeTree".to_string()), + Log::Debug(&format!( + "Node \"{}\" added to the scene as the root of the NodeTree! Unique ID of \"{}\" generated!", + root.name(), root.rid() + ))); + self.nodes.push(node); + } + }); } /// Runs the starting process behaviour - @@ -418,14 +431,21 @@ impl NodeTreeBase { /// Registers the node to the tree and gives it a unique RID. /// This should not be used manually. - pub unsafe fn register_node(&mut self, node: Box) -> RID { - let rid: RID = self.nodes.push(Box::into_raw(node)); + /// + /// # Safety + /// Assumes that the pointer was created from a box like so: + /// ```rust,ignore + /// Box::into_raw(Box::new(node)) + /// ``` + pub unsafe fn register_node(&mut self, node: *mut dyn Node) -> RID { + let rid: RID = self.nodes.push(node); self.identity.insert(rid, NodeIdentity::NodePath); rid } - /// Unregisters a node from the tree, returning the Node if it existed. + /// Unregisters a node from the tree, returning the Node as a `Box` if it existed. /// This should not be used manually. + /// /// # Note /// This does not check if the Node was a singleton and thus cannot be unregistered. pub unsafe fn unregister_node(&mut self, rid: RID) -> Option> { @@ -508,3 +528,18 @@ impl NodeGetter for String { tree.singletons.get(self).copied() } } + + +/// Initializes the base `NodeTreeBase` field in a `NodeTree` inherited object. +/// +/// # Safety +/// It is UNDEFINED behaviour to NOT call this function within a tree implementation's constructor. +pub fn initialize_base(tree: &mut Box, scene: I, verbosity: LoggerVerbosity) { + let base: NodeTreeBase = unsafe { NodeTreeBase::new(verbosity) }; + unsafe { + tree.set_base(base); + + let tree_ptr: *mut dyn NodeTree = tree.as_dyn_raw_mut(); + tree.base_mut().initialize(tree_ptr, scene); + } +} diff --git a/node_tree_core/src/traits/instanceable.rs b/node_tree_core/src/traits/instanceable.rs new file mode 100644 index 0000000..dce814c --- /dev/null +++ b/node_tree_core/src/traits/instanceable.rs @@ -0,0 +1,12 @@ +use super::node::Node; + + + +/// This marks any object that can be referenced in the `NodeTree` as either a node or a collection +/// of nodes. +pub trait Instanceable { + + /// Goes through and iterates through all of the nodes that are represented by this collection. + /// The arguments passed through are the pointers to the parent (if there is one) and the node. + fn iterate, *mut dyn Node)>(self, iterator: F); +} diff --git a/node_tree_core/src/traits/mod.rs b/node_tree_core/src/traits/mod.rs index 5aa6fb3..f5fe2e6 100644 --- a/node_tree_core/src/traits/mod.rs +++ b/node_tree_core/src/traits/mod.rs @@ -1,3 +1,4 @@ pub mod node; pub mod node_getter; pub mod node_tree; +pub mod instanceable; diff --git a/node_tree_core/src/traits/node.rs b/node_tree_core/src/traits/node.rs index 17b60af..2bcd213 100644 --- a/node_tree_core/src/traits/node.rs +++ b/node_tree_core/src/traits/node.rs @@ -26,6 +26,7 @@ use std::any::Any; use std::ops::{ Deref, DerefMut }; use crate::structs::{ node_base::NodeBase, node_tree_base::ProcessMode }; +use super::instanceable::Instanceable; /// This implements of of the node's abstract behaviours. @@ -88,3 +89,9 @@ pub trait Node: NodeAbstract { ProcessMode::Inherit } } + +impl Instanceable for N { + fn iterate, *mut dyn Node)>(self, mut iterator: F) { + iterator(None, Box::into_raw(self.to_dyn_box())); + } +} diff --git a/node_tree_core/src/traits/node_tree.rs b/node_tree_core/src/traits/node_tree.rs index 7a6a6ab..3eff24f 100644 --- a/node_tree_core/src/traits/node_tree.rs +++ b/node_tree_core/src/traits/node_tree.rs @@ -26,8 +26,6 @@ use std::any::Any; use std::ops::{ Deref, DerefMut }; -use super::node::Node; -use crate::structs::logger::LoggerVerbosity; use crate::structs::node_tree_base::NodeTreeBase; @@ -39,6 +37,29 @@ use crate::structs::node_tree_base::NodeTreeBase; /// Every application that wishes to take advantage of the `NodeTree` system must have its root /// struct inherit from this. +/// +/// # Example +/// Here is an example implementation: +/// ```rust +/// use node_tree::prelude::*; +/// +/// #[derive(Debug, Tree)] +/// pub struct TreeSimple { +/// base: Option +/// } +/// +/// impl TreeSimple { +/// pub fn new(scene: I, verbosity: LoggerVerbosity) -> Box { +/// let mut tree: Box = Box::new(TreeSimple { +/// base: None +/// }); +/// +/// initialize_base(&mut tree, scene, verbosity); // Not running this will cause undefined behaviour! +/// tree +/// } +/// } +/// ``` +/// Note that the `Tree` should be initialized on the heap, preferably with a Box pointer. pub trait NodeTree: Deref + DerefMut + Any { /// Sets the `NodeTreeBase` struct. @@ -68,13 +89,3 @@ pub trait NodeTree: Deref + DerefMut + Any { /// Converts this into a mutable Any type. fn as_any_mut(&mut self) -> &mut dyn Any; } - - -/// Initializes the base `NodeTreeBase` field in a `NodeTree` inherited object. -pub fn init_base(tree: &mut Box, root: N, verbosity: LoggerVerbosity) { - let base: NodeTreeBase = unsafe { NodeTreeBase::new(tree.as_dyn_mut(), root, verbosity) }; - unsafe { - tree.set_base(base); - } -} - diff --git a/node_tree_core/src/trees/tree_simple.rs b/node_tree_core/src/trees/tree_simple.rs index 4fddddf..424ff0e 100644 --- a/node_tree_core/src/trees/tree_simple.rs +++ b/node_tree_core/src/trees/tree_simple.rs @@ -26,12 +26,13 @@ //! Contains a simply implementation of a `NodeTree`. //! +use core::panic; use std::any::Any; use std::ops::{ Deref, DerefMut }; use crate::structs::logger::LoggerVerbosity; -use crate::structs::node_tree_base::NodeTreeBase; -use crate::traits::{ node::Node, node_tree::{ NodeTree, init_base } }; +use crate::structs::node_tree_base::{ NodeTreeBase, initialize_base }; +use crate::traits::{ instanceable::Instanceable, node_tree::NodeTree }; /// A simple implementation of a `NodeTree` which will work just fine for most applications that do @@ -44,12 +45,12 @@ pub struct TreeSimple { impl TreeSimple { /// Creates a new `TreeSimple` structure. - pub fn new(root: N, verbosity: LoggerVerbosity) -> Box { + pub fn new(scene: I, verbosity: LoggerVerbosity) -> Box { let mut tree: Box = Box::new(TreeSimple { base: None }); - init_base(&mut tree, root, verbosity); + initialize_base(&mut tree, scene, verbosity); tree } } @@ -107,6 +108,10 @@ impl Deref for TreeSimple { impl DerefMut for TreeSimple { fn deref_mut(&mut self) -> &mut Self::Target { + if self.base.is_none() { + panic!("None!"); + } + unsafe { self.base.as_mut().unwrap_unchecked() } diff --git a/node_tree_core/tests/node.rs b/node_tree_core/tests/node.rs index fb82ec0..8e44240 100644 --- a/node_tree_core/tests/node.rs +++ b/node_tree_core/tests/node.rs @@ -10,9 +10,65 @@ fn test_node_integration() { std::env::set_var("RUST_BACKTRACE", "1"); } + // Initialize the NodeScene. + let scene: NodeScene = scene! { + NodeA("Root") { + NodeA("1_Node") { + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + } + }, + NodeA("1_Node") { + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + } + }, + NodeA("1_Node") { + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + } + } + } + }; + // Create the tree. - let root: NodeA = NodeA::new("Root".to_string()); - let mut tree: Box = TreeSimple::new(root, LoggerVerbosity::NoDebug); + let mut tree: Box = TreeSimple::new(scene, LoggerVerbosity::NoDebug); // Begin operations on the tree. tree.start(); @@ -30,20 +86,13 @@ pub struct NodeA { } impl NodeA { - fn new(name: String) -> Self { - NodeA { base: NodeBase::new(name) } + fn new(name: &str) -> Self { + NodeA { base: NodeBase::new(name.to_string()) } } } impl Node for NodeA { fn ready(&mut self) -> () { - if self.depth() < 3 { - let depth_new: usize = self.depth() + 1; - - self.add_child(NodeA::new(format!("{}_Node", depth_new))); - self.add_child(NodeA::new(format!("{}_Node", depth_new))); - self.add_child(NodeA::new(format!("{}_Node", depth_new))); - } if self.is_root() { println!("{:?}", self.children()); } diff --git a/node_tree_derive/Cargo.toml b/node_tree_derive/Cargo.toml index 75f4434..18026d9 100644 --- a/node_tree_derive/Cargo.toml +++ b/node_tree_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node_tree_derive" -version = "0.3.0" +version = "0.4.0" edition = "2018" exclude = [ @@ -18,5 +18,6 @@ license = "MIT OR Apache-2.0" proc-macro = true [dependencies] -syn = "1.0" -quote = "1.0" +syn = { version = "1.0", features = ["parsing", "full"] } +quote = "1.0" +proc-macro2 = "1.0" diff --git a/node_tree_derive/README.md b/node_tree_derive/README.md index f61b7b8..1a10ffa 100644 --- a/node_tree_derive/README.md +++ b/node_tree_derive/README.md @@ -106,6 +106,34 @@ fn main() -> () { } ``` +You may also input a `NodeScene` when initializing a `NodeTree` or adding a child via `add_child`: +```rust +use node_tree::prelude::*; + + +let scene: NodeScene = scene! { + NodeA("Root") { // Nodes must have a constructor named `new()` in order for this to work! + NodeA("1_Node") { + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + }, + NodeA("2_Node") { + NodeA("3_Node"), + NodeA("3_Node"), + NodeA("3_Node") + } + } + } +}; +``` + Logging is also supported. Here is an example setup with an output of a few warnings and a crash. Note that the crash header/footer are customizable, and that the output is actually colored in a real terminal. ```rust /// Root Node @@ -228,4 +256,4 @@ Goodbye World! (Program Exited) - 👪 The ability to manage nodes with `add_child()` and `remove_child()`. - 📝 Includes a dynamic logging system that is deeply integrated with the node framework. - 🌲 Allows for the direct referencing of the `NodeTree` through a node's `root()` function. -- 📜 TODO: Includes a method to save and handle individual node scenes, such as the handy visual macro `Scene!`. +- 📜 Includes a method to save (TODO) and handle individual node scenes, such as the handy visual macro `scene!`. diff --git a/node_tree_derive/src/lib.rs b/node_tree_derive/src/lib.rs index e567d7f..83d85d4 100644 --- a/node_tree_derive/src/lib.rs +++ b/node_tree_derive/src/lib.rs @@ -30,7 +30,15 @@ extern crate proc_macro; use proc_macro::TokenStream; use quote::quote; -use syn::{ parse_macro_input, DeriveInput, Ident }; +use syn::{ parse_macro_input, DeriveInput, Ident, parse::{ Parse, ParseStream }, Token }; +use proc_macro2::TokenStream as TokenStream2; + + +/* + * Node + * Abstract + */ + /// Implements all of the required traits for a `Node` type to be created aside from the `Node` /// trait itself, which needs to be implemented manually. @@ -101,6 +109,13 @@ pub fn r#abstract(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } + +/* + * Tree + * Abstract + */ + + /// Implements all of the required traits for a `NodeTree` type to be created. #[proc_macro_derive(Tree)] pub fn tree(input: TokenStream) -> TokenStream { @@ -176,3 +191,77 @@ pub fn tree(input: TokenStream) -> TokenStream { // Return the generated impl as a TokenStream TokenStream::from(expanded) } + + +/* + * Scene + * Macro + */ + + +struct SceneNode { + node_type: Ident, + params: syn::ExprParen, + children: Vec, +} + +impl Parse for SceneNode { + fn parse(input: ParseStream) -> syn::Result { + let node_type: Ident = input.parse()?; + let params: syn::ExprParen = input.parse()?; + + let mut children: Vec = Vec::new(); + if input.peek(syn::token::Brace) { + let content; + syn::braced!(content in input); + + while !content.is_empty() { + children.push(content.parse()?); + if !content.is_empty() { + content.parse::()?; + } + } + } + + Ok(SceneNode { + node_type, + params, + children, + }) + } +} + +fn generate_node(node: &SceneNode) -> TokenStream2 { + let node_type: &Ident = &node.node_type; + let params: &syn::ExprParen = &node.params; + let children: Vec = node.children.iter().map(generate_node).collect(); + + quote! { + { + let mut scene: NodeScene = NodeScene::new(#node_type::new #params); + #( + scene.append(#children); + )* + scene + } + } +} + +/// Allows for the easy implementation of a `NodeScene`, like so: +/// ```rust,ignore +/// use node_tree::prelude::*; +/// +/// let scene: NodeScene = scene! { +/// Node1(param1, param2) { +/// Node2(param1), +/// Node3() +/// }, +/// Node4() +/// }; +/// ``` +#[proc_macro] +pub fn scene(input: TokenStream) -> TokenStream { + let root: SceneNode = parse_macro_input!(input as SceneNode); + let expanded: TokenStream2 = generate_node(&root); + TokenStream::from(expanded) +}