Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge Visit and NoVisit derive macros #294

Merged
merged 3 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 8 additions & 23 deletions crates/modor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
//!
//! #[derive(Default, Visit)]
//! struct Counter {
//! #[modor(skip)] // u32 does not implement `Node` trait, so it cannot be visited
//! value: u32,
//! }
//!
Expand All @@ -66,6 +65,8 @@ pub use wasm_bindgen_test;

mod app;
mod globals;
#[doc(hidden)]
pub mod macro_utils;
mod node;
mod platform;

Expand Down Expand Up @@ -136,48 +137,32 @@ pub use modor_derive::test;
///
/// The type must implement [`Default`] trait.
///
/// Both structs and enums are supported.
///
/// # Examples
///
/// ```rust
/// # use modor::*;
/// #
/// #[derive(Default, RootNode, Node, Visit)]
/// struct Root {
/// #[modor(skip)]
/// value: u32,
/// }
/// ```
pub use modor_derive::RootNode;

/// Implements [`Node`].
///
/// Both structs and enums are supported.
///
/// # Examples
///
/// See [`RootNode`](macro@crate::RootNode).
pub use modor_derive::Node;

/// Implements [`Visit`] in case there is no inner node.
///
/// This macro can be used instead of [`Visit`](macro@crate::Visit) when there is no inner node.
/// This avoids unnecessary usage of `#[modor(skip)]` in case all fields should be skipped.
///
/// # Examples
///
/// ```rust
/// # use modor::*;
/// #
/// #[derive(Default, RootNode, Node, NoVisit)]
/// struct Root {
/// value: u32,
/// }
/// ```
pub use modor_derive::NoVisit;

/// Implements [`Visit`] so that inner node are visited.
/// Implements [`Visit`] so that inner fields implementing [`Node`] are visited.
///
/// `#[modor(skip)]` can be added on a field to skip its automatic update, for example because:
/// - the field type doesn't implement [`Node`].
/// - the field is a node but shouldn't be updated for performance reasons.
/// Both structs and enums are supported.
///
/// # Examples
///
Expand Down
24 changes: 24 additions & 0 deletions crates/modor/src/macro_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::{Context, Node};

pub struct MaybeNode<'a, T>(pub &'a mut T);

impl<T> MaybeNode<'_, T>
where
T: Node,
{
#[inline]
pub fn update(&mut self, ctx: &mut Context<'_>) {
Node::update(self.0, ctx);
}
}

pub trait NotNode {
fn update(&mut self, ctx: &mut Context<'_>);
}

impl<T> NotNode for MaybeNode<'_, T> {
#[inline]
fn update(&mut self, _ctx: &mut Context<'_>) {
// do nothing
}
}
65 changes: 64 additions & 1 deletion crates/modor/src/node.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::Context;
use std::collections::HashMap;
use std::ops::DerefMut;
use std::ops::{Deref, DerefMut};

/// A trait for defining a root node accessible from anywhere during update.
///
Expand Down Expand Up @@ -53,6 +53,18 @@ pub trait Node: Visit {
self.visit(ctx);
self.on_exit(ctx);
}

/// Converts the node into a [`Const<Self>`].
///
/// This method runs [`Node::update`] before making the conversion.
#[inline]
fn into_const(mut self, ctx: &mut Context<'_>) -> Const<Self>
where
Self: Sized,
{
self.update(ctx);
Const { inner: self }
}
}

/// A trait for defining the visit of inner nodes.
Expand All @@ -76,6 +88,18 @@ impl Visit for Box<dyn Node> {
}
}

impl<T> Node for Box<T> where T: Node {}

impl<T> Visit for Box<T>
where
T: Node,
{
#[inline]
fn visit(&mut self, ctx: &mut Context<'_>) {
self.deref_mut().update(ctx);
}
}

impl<T> Node for Option<T> where T: Node {}

impl<T> Visit for Option<T>
Expand Down Expand Up @@ -116,3 +140,42 @@ where
}
}
}

/// A wrapper on a constant node.
///
/// This type is mainly used for optimization purpose for cases where the [`Node::update`] has no
/// effect because the node is never mutated.
///
/// # Examples
///
/// ```rust
/// # use modor::*;
/// #
/// #[derive(Node, Visit)]
/// struct Root {
/// constant: Const<Value>,
/// }
///
/// impl RootNode for Root {
/// fn on_create(ctx: &mut Context<'_>) -> Self {
/// Self {
/// constant: Value(42).into_const(ctx),
/// }
/// }
/// }
///
/// #[derive(Node, Visit)]
/// struct Value(u32);
/// ```
#[derive(Debug)]
pub struct Const<T> {
inner: T,
}

impl<T> Deref for Const<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.inner
}
}
4 changes: 2 additions & 2 deletions crates/modor/tests/integration/app.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use log::Level;
use modor::App;
use modor_derive::{NoVisit, Node, RootNode};
use modor_derive::{Node, RootNode, Visit};

#[modor::test]
fn create_node_handle() {
Expand All @@ -11,7 +11,7 @@ fn create_node_handle() {
assert_eq!(handle.get_mut(&mut ctx).value, 0);
}

#[derive(Default, RootNode, Node, NoVisit)]
#[derive(Default, RootNode, Node, Visit)]
struct Root {
value: usize,
}
4 changes: 2 additions & 2 deletions crates/modor/tests/integration/node/box_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ fn update_node() {
}

#[derive(Default, RootNode, Node, Visit)]
struct Container(#[modor(skip)] Vec<&'static str>);
struct Container(Vec<&'static str>);

#[derive(Node, Visit)]
struct Root(Box<dyn Node>);
struct Root(Box<InnerNode>);

impl RootNode for Root {
fn on_create(_ctx: &mut Context<'_>) -> Self {
Expand Down
45 changes: 45 additions & 0 deletions crates/modor/tests/integration/node/const_node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use log::Level;
use modor::{App, Const, Context, Node, RootNode, Visit};

#[modor::test]
fn update_node() {
let mut app = App::new::<Root>(Level::Info);
app.update();
app.update();
app.update();
let container = app.root::<Container>();
assert_eq!(container.0, ["InnerNode::on_enter", "InnerNode::on_exit"]);
}

#[derive(Default, RootNode, Node, Visit)]
struct Container(Vec<&'static str>);

#[derive(Node, Visit)]
struct Root(Const<InnerNode>);

impl RootNode for Root {
fn on_create(ctx: &mut Context<'_>) -> Self {
let constant = InnerNode(42).into_const(ctx);
assert_eq!(constant.0, 42);
Self(constant)
}
}

#[derive(Visit)]
struct InnerNode(u32);

impl Node for InnerNode {
fn on_enter(&mut self, ctx: &mut Context<'_>) {
ctx.root::<Container>()
.get_mut(ctx)
.0
.push("InnerNode::on_enter");
}

fn on_exit(&mut self, ctx: &mut Context<'_>) {
ctx.root::<Container>()
.get_mut(ctx)
.0
.push("InnerNode::on_exit");
}
}
41 changes: 41 additions & 0 deletions crates/modor/tests/integration/node/dyn_box_node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use log::Level;
use modor::{App, Context, Node, RootNode, Visit};

#[modor::test]
fn update_node() {
let mut app = App::new::<Root>(Level::Info);
app.update();
let container = app.root::<Container>();
assert_eq!(container.0, ["InnerNode::on_enter", "InnerNode::on_exit"]);
}

#[derive(Default, RootNode, Node, Visit)]
struct Container(Vec<&'static str>);

#[derive(Node, Visit)]
struct Root(Box<dyn Node>);

impl RootNode for Root {
fn on_create(_ctx: &mut Context<'_>) -> Self {
Self(Box::new(InnerNode))
}
}

#[derive(Visit)]
struct InnerNode;

impl Node for InnerNode {
fn on_enter(&mut self, ctx: &mut Context<'_>) {
ctx.root::<Container>()
.get_mut(ctx)
.0
.push("InnerNode::on_enter");
}

fn on_exit(&mut self, ctx: &mut Context<'_>) {
ctx.root::<Container>()
.get_mut(ctx)
.0
.push("InnerNode::on_exit");
}
}
6 changes: 3 additions & 3 deletions crates/modor/tests/integration/node/enum_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn update_node() {
}

#[derive(Default, RootNode, Node, Visit)]
struct Container(#[modor(skip)] Vec<&'static str>);
struct Container(Vec<&'static str>);

#[derive(Node, Visit)]
struct Root(TestNode);
Expand All @@ -33,9 +33,9 @@ impl RootNode for Root {
#[derive(Visit)]
enum TestNode {
Node(InnerNode),
#[modor(skip)]
NotNode(usize),
#[modor(skip)]
StructNode { node: InnerNode },
StructNoNode { value: usize },
Empty,
}

Expand Down
4 changes: 2 additions & 2 deletions crates/modor/tests/integration/node/hashmap_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn update_node() {
}

#[derive(Default, RootNode, Node, Visit)]
struct Container(#[modor(skip)] Vec<String>);
struct Container(Vec<String>);

#[derive(Node, Visit)]
struct Root(HashMap<usize, InnerNode>);
Expand All @@ -32,7 +32,7 @@ impl RootNode for Root {
}

#[derive(Visit)]
struct InnerNode(#[modor(skip)] usize);
struct InnerNode(usize);

impl Node for InnerNode {
fn on_enter(&mut self, ctx: &mut Context<'_>) {
Expand Down
2 changes: 2 additions & 0 deletions crates/modor/tests/integration/node/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pub mod box_node;
pub mod const_node;
pub mod dyn_box_node;
pub mod enum_node;
pub mod hashmap_node;
pub mod option_node;
Expand Down
2 changes: 1 addition & 1 deletion crates/modor/tests/integration/node/option_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn update_node_without_inner_node() {
}

#[derive(Default, RootNode, Node, Visit)]
struct Container(#[modor(skip)] Vec<&'static str>);
struct Container(Vec<&'static str>);

#[derive(Node, Visit)]
struct Root<const IS_SOME: bool>(Option<InnerNode>);
Expand Down
3 changes: 1 addition & 2 deletions crates/modor/tests/integration/node/struct_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn update_node() {
}

#[derive(Default, RootNode, Node, Visit)]
struct Container(#[modor(skip)] Vec<&'static str>);
struct Container(Vec<&'static str>);

#[derive(Node, Visit)]
struct Root(TestNode);
Expand All @@ -35,7 +35,6 @@ impl RootNode for Root {
#[derive(Visit)]
struct TestNode {
node: InnerNode,
#[modor(skip)]
_not_node: usize,
}

Expand Down
4 changes: 2 additions & 2 deletions crates/modor/tests/integration/node/tuple_struct_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn update_node() {
}

#[derive(Default, RootNode, Node, Visit)]
struct Container(#[modor(skip)] Vec<&'static str>);
struct Container(Vec<&'static str>);

#[derive(Node, Visit)]
struct Root(TestNode);
Expand All @@ -31,7 +31,7 @@ impl RootNode for Root {

#[allow(unused_tuple_struct_fields)]
#[derive(Visit)]
struct TestNode(InnerNode, #[modor(skip)] usize);
struct TestNode(InnerNode, usize);

impl Node for TestNode {
fn on_enter(&mut self, ctx: &mut Context<'_>) {
Expand Down
Loading
Loading