From 024a07ff3db5ec09233f99b6fc34600c068dec97 Mon Sep 17 00:00:00 2001 From: Thiago Ramos <99thiagodejesus@gmail.com> Date: Mon, 19 May 2025 21:43:31 -0300 Subject: [PATCH 1/5] refactor: apply rust api guidelines --- Readme.md => README.md | 0 src/grid_events.rs | 2 +- src/inner_grid.rs | 2 +- src/utils.rs | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename Readme.md => README.md (100%) diff --git a/Readme.md b/README.md similarity index 100% rename from Readme.md rename to README.md diff --git a/src/grid_events.rs b/src/grid_events.rs index 6cdf7be..8be9e08 100644 --- a/src/grid_events.rs +++ b/src/grid_events.rs @@ -48,7 +48,7 @@ use std::fmt::Debug; /// /// This structure is passed to event listeners whenever changes occur in the grid, /// providing details about what changes were made. -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct ChangesEventValue { /// Vector of changes that occurred in the grid pub changes: Vec, diff --git a/src/inner_grid.rs b/src/inner_grid.rs index de0c194..f9646c7 100644 --- a/src/inner_grid.rs +++ b/src/inner_grid.rs @@ -46,7 +46,7 @@ pub enum UpdateGridOperation { /// The grid maintains a 2D layout of cells, where each cell can either be /// empty (None) or contain a node ID (Some(String)). The grid can dynamically /// expand vertically to accommodate new nodes. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] pub struct InnerGrid { /// Whether the grid can expand vertically (add rows) can_expand_y: bool, diff --git a/src/utils.rs b/src/utils.rs index abd946a..5d266e5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -30,7 +30,7 @@ use crate::error::InnerGridError; /// This struct defines a rectangular area in the grid by specifying: /// - The top-left corner position (x, y) /// - The width and height of the rectangle -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub struct ForCellArgs { /// X coordinate of the top-left corner pub x: usize, From 19069f92fa2681d5c6e582a070b62ad258e44663 Mon Sep 17 00:00:00 2001 From: Thiago Ramos <99thiagodejesus@gmail.com> Date: Wed, 21 May 2025 20:29:17 -0300 Subject: [PATCH 2/5] docs: improve docs --- .gitignore | 1 + Cargo.toml | 1 - src/grid_engine.rs | 86 +++++++++++++++++++++++++++++++++++++++++----- src/grid_events.rs | 20 ++++++++--- src/lib.rs | 9 +++-- 5 files changed, 101 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index ea8c4bf..145fcd0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +.local \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 734fa8b..c907efe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ edition = "2024" authors = ["Thiago Ramos"] description = "A flexible and efficient library for managing 2D grid-based layouts with automatic collision handling and dynamic vertical expansion" documentation = "https://docs.rs/grid-engine" -homepage = "https://github.com/thiagodejesus/grid-engine" repository = "https://github.com/thiagodejesus/grid-engine" readme = "README.md" keywords = ["grid", "layout", "collision", "2d", "dashboard"] diff --git a/src/grid_engine.rs b/src/grid_engine.rs index 9044060..7566adf 100644 --- a/src/grid_engine.rs +++ b/src/grid_engine.rs @@ -30,24 +30,29 @@ //! //! ``` //! use grid_engine::grid_engine::GridEngine; +//! # use std::error::Error; //! +//! # fn main() -> Result<(), Box> { //! let mut grid = GridEngine::new(10, 12); //! //! // Add items to the grid -//! grid.add_item("item1".to_string(), 2, 2, 2, 4).unwrap(); +//! grid.add_item("item1".to_string(), 2, 2, 2, 4)?; //! //! // Move items (handles collisions automatically) -//! grid.move_item("item1", 4, 4).unwrap(); +//! grid.move_item("item1", 4, 4)?; //! //! // Remove items -//! grid.remove_item("item1").unwrap(); +//! grid.remove_item("item1")?; +//! # +//! # Ok(()) +//! # } //! ``` use crate::error::{GridEngineError, InnerGridError, ItemError}; use crate::grid_events::{ChangesEventValue, GridEvents}; use crate::inner_grid::{InnerGrid, UpdateGridOperation}; use crate::node::Node; -use crate::utils::{for_cell, ForCellArgs}; +use crate::utils::{ForCellArgs, for_cell}; use std::{collections::BTreeMap, fmt::Debug}; /// Represents data for an item addition change @@ -131,17 +136,37 @@ impl GridEngine { } } + /// Creates a new node with the specified parameters. fn new_node(&mut self, id: String, x: usize, y: usize, w: usize, h: usize) -> Node { let node = Node::new(id, x, y, w, h); node } + /// Creates a change operation to add a new node to the grid. fn create_add_change(&mut self, node: Node) { self.pending_changes .push(Change::Add(AddChangeData { value: node })); } /// Get the nodes sorted by id + /// + /// # Example + /// + /// ``` + /// use grid_engine::grid_engine::GridEngine; + /// + /// # fn main() -> Result<(), Box> { + /// let mut grid = GridEngine::new(10, 10); + /// grid.add_item("b".to_string(), 0, 0, 2, 2).unwrap(); + /// grid.add_item("a".to_string(), 0, 2, 2, 2).unwrap(); + /// + /// let nodes = grid.get_nodes(); + /// assert_eq!(nodes.len(), 2); + /// assert_eq!(nodes[0].id, "a"); + /// assert_eq!(nodes[1].id, "b"); + /// # Ok(()) + /// # } + /// ``` pub fn get_nodes(&self) -> Vec { let mut cloned: Vec = self.items.values().cloned().collect(); // Would be better to sort by some created_at @@ -158,6 +183,20 @@ impl GridEngine { /// # Returns /// /// A reference to the InnerGrid instance + /// + /// # Example + /// + /// ``` + /// use grid_engine::grid_engine::GridEngine; + /// use std::error::Error; + /// + /// # fn main() -> Result<(), Box> { + /// let grid = GridEngine::new(10, 10); + /// let inner_grid = grid.get_inner_grid(); + /// assert_eq!(inner_grid.rows(), 10); + /// assert_eq!(inner_grid.cols(), 10); + /// # Ok(()) + /// # } pub fn get_inner_grid(&self) -> &InnerGrid { &self.grid } @@ -184,9 +223,19 @@ impl GridEngine { /// /// ``` /// use grid_engine::grid_engine::GridEngine; + /// use std::error::Error; /// + /// # fn main() -> Result<(), Box> { /// let mut grid = GridEngine::new(10, 10); - /// grid.add_item("box1".to_string(), 0, 0, 2, 2).unwrap(); // 2x2 item at top-left + /// grid.add_item("box1".to_string(), 0, 0, 2, 2)?; // 2x2 item at top-left + /// + /// // Check if the item was added correctly + /// let item = grid.get_nodes(); + /// assert_eq!(item.len(), 1); + /// assert_eq!(item[0].id, "box1"); + /// + /// # Ok(()) + /// # } /// ``` pub fn add_item( &mut self, @@ -240,10 +289,16 @@ impl GridEngine { /// /// ``` /// use grid_engine::grid_engine::GridEngine; + /// use std::error::Error; + /// + /// # fn main() -> Result<(), Box> { /// /// let mut grid = GridEngine::new(10, 10); - /// grid.add_item("box1".to_string(), 0, 0, 2, 2).unwrap(); - /// grid.remove_item("box1").unwrap(); // Removes the item + /// grid.add_item("box1".to_string(), 0, 0, 2, 2)?; + /// grid.remove_item("box1")?; // Removes the item + /// + /// # Ok(()) + /// # } /// ``` pub fn remove_item(&mut self, id: &str) -> Result { let node = match self.items.get(id) { @@ -410,10 +465,23 @@ impl GridEngine { /// /// ``` /// use grid_engine::grid_engine::GridEngine; + /// # use std::error::Error; /// + /// # fn main() -> Result<(), Box> { + /// /// let mut grid = GridEngine::new(10, 10); - /// grid.add_item("box1".to_string(), 0, 0, 2, 2).unwrap(); - /// grid.move_item("box1", 2, 2).unwrap(); // Moves box to position 2,2 + /// grid.add_item("box1".to_string(), 0, 0, 2, 2)?; + /// grid.move_item("box1", 2, 2)?; // Moves box to position 2,2 + /// + /// // Check if the item was moved correctly + /// let item = grid.get_nodes(); + /// assert_eq!(item.len(), 1); + /// assert_eq!(item[0].x, 2); + /// assert_eq!(item[0].y, 2); + /// + /// # Ok(()) + /// # } + /// /// ``` pub fn move_item( &mut self, diff --git a/src/grid_events.rs b/src/grid_events.rs index 8be9e08..0052055 100644 --- a/src/grid_events.rs +++ b/src/grid_events.rs @@ -139,10 +139,22 @@ impl GridEvents { /// /// let mut grid = GridEngine::new(10, 10); /// let listener_id = grid.events.add_changes_listener(Box::new(|_| {})); - /// grid.events.remove_changes_listener(&listener_id); // Listener removed + /// let removed = grid.events.remove_changes_listener(&listener_id); // Listener removed + /// + /// assert!(removed.is_some()); + /// /// ``` - pub fn remove_changes_listener(&mut self, id: &str) { - self.changes_listeners.retain(|listener| listener.id != id); + pub fn remove_changes_listener(&mut self, id: &str) -> Option { + if let Some(pos) = self + .changes_listeners + .iter() + .position(|listener| listener.id == id) + { + let listener = self.changes_listeners.remove(pos); + Some(listener.function) + } else { + None + } } /// Triggers the change event, notifying all registered listeners. @@ -154,7 +166,7 @@ impl GridEvents { /// # Arguments /// /// * `value` - The event data containing information about the changes - pub fn trigger_changes_event(&mut self, value: &ChangesEventValue) { + pub(crate) fn trigger_changes_event(&mut self, value: &ChangesEventValue) { for listener in &mut self.changes_listeners { (listener.function)(value); } diff --git a/src/lib.rs b/src/lib.rs index 10f805a..40631e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,15 +30,20 @@ //! //! ```rust //! use grid_engine::grid_engine::GridEngine; +//! # use std::error::Error; //! +//! # fn main() -> Result<(), Box> { //! // Create a new 10x12 grid //! let mut grid = GridEngine::new(10, 12); //! //! // Add items (automatic collision handling) -//! grid.add_item("box1".to_string(), 0, 0, 2, 2).unwrap(); +//! grid.add_item("box1".to_string(), 0, 0, 2, 2)?; //! //! // Move items -//! grid.move_item("box1", 2, 2).unwrap(); +//! grid.move_item("box1", 2, 2)?; +//! +//! # Ok(()) +//! # } //! ``` //! //! See the `examples` directory for more usage examples. From 1ea4254e40cea5a4508bcf0ff60d16a8b254d25d Mon Sep 17 00:00:00 2001 From: Thiago Ramos <99thiagodejesus@gmail.com> Date: Wed, 21 May 2025 21:03:55 -0300 Subject: [PATCH 3/5] refactor: adjust pub --- examples/managing_grid.rs | 2 +- src/grid_engine.rs | 113 ++++++++++++++++++++++++++++++++------ src/grid_events.rs | 53 +++++++++++++----- src/inner_grid.rs | 6 +- src/node.rs | 37 +++++++++++-- 5 files changed, 170 insertions(+), 41 deletions(-) diff --git a/examples/managing_grid.rs b/examples/managing_grid.rs index fa15c3e..ebd7ddd 100644 --- a/examples/managing_grid.rs +++ b/examples/managing_grid.rs @@ -55,7 +55,7 @@ fn main() { let mut grid = GridEngine::new(10, 12); - grid.events.add_changes_listener(Box::new(|event| { + grid.events_mut().add_changes_listener(Box::new(|event| { println!("Event triggered: {:#?}", event); })); diff --git a/src/grid_engine.rs b/src/grid_engine.rs index 7566adf..b1a23bf 100644 --- a/src/grid_engine.rs +++ b/src/grid_engine.rs @@ -59,23 +59,92 @@ use std::{collections::BTreeMap, fmt::Debug}; #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct AddChangeData { /// The node being added to the grid - pub value: Node, + value: Node, +} + +impl AddChangeData { + /// Creates a new AddChangeData instance + /// + /// # Arguments + /// + /// * `value` - The node being added to the grid + /// + /// # Returns + /// + /// A new instance of AddChangeData + pub fn new(value: Node) -> Self { + Self { value } + } + + /// Returns the node being added + pub fn value(&self) -> &Node { + &self.value + } } /// Represents data for an item removal change #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct RemoveChangeData { /// The node being removed from the grid - pub value: Node, + value: Node, +} + +impl RemoveChangeData { + /// Creates a new RemoveChangeData instance + /// + /// # Arguments + /// + /// * `value` - The node being removed from the grid + /// + /// # Returns + /// + /// A new instance of RemoveChangeData + pub fn new(value: Node) -> Self { + Self { value } + } + + /// Returns the node being removed + pub fn value(&self) -> &Node { + &self.value + } } /// Represents data for an item movement change #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct MoveChangeData { /// The original state of the node - pub old_value: Node, + old_value: Node, /// The new state of the node after movement - pub new_value: Node, + new_value: Node, +} + +impl MoveChangeData { + /// Creates a new MoveChangeData instance + /// + /// # Arguments + /// + /// * `old_value` - The original state of the node + /// * `new_value` - The new state of the node after movement + /// + /// # Returns + /// + /// A new instance of MoveChangeData + pub fn new(old_value: Node, new_value: Node) -> Self { + Self { + old_value, + new_value, + } + } + + /// Returns the original state of the node + pub fn old_value(&self) -> &Node { + &self.old_value + } + + /// Returns the new state of the node after movement + pub fn new_value(&self) -> &Node { + &self.new_value + } } /// Represents different types of changes that can occur in the grid @@ -109,7 +178,7 @@ pub struct GridEngine { /// Changes waiting to be applied pending_changes: Vec, /// Event system for tracking grid changes - pub events: GridEvents, + events: GridEvents, } impl GridEngine { @@ -162,8 +231,8 @@ impl GridEngine { /// /// let nodes = grid.get_nodes(); /// assert_eq!(nodes.len(), 2); - /// assert_eq!(nodes[0].id, "a"); - /// assert_eq!(nodes[1].id, "b"); + /// assert_eq!(nodes[0].id(), "a"); + /// assert_eq!(nodes[1].id(), "b"); /// # Ok(()) /// # } /// ``` @@ -232,7 +301,7 @@ impl GridEngine { /// // Check if the item was added correctly /// let item = grid.get_nodes(); /// assert_eq!(item.len(), 1); - /// assert_eq!(item[0].id, "box1"); + /// assert_eq!(item[0].id(), "box1"); /// /// # Ok(()) /// # } @@ -468,20 +537,20 @@ impl GridEngine { /// # use std::error::Error; /// /// # fn main() -> Result<(), Box> { - /// + /// /// let mut grid = GridEngine::new(10, 10); /// grid.add_item("box1".to_string(), 0, 0, 2, 2)?; /// grid.move_item("box1", 2, 2)?; // Moves box to position 2,2 - /// + /// /// // Check if the item was moved correctly /// let item = grid.get_nodes(); /// assert_eq!(item.len(), 1); - /// assert_eq!(item[0].x, 2); - /// assert_eq!(item[0].y, 2); - /// + /// assert_eq!(item[0].x(), &2); + /// assert_eq!(item[0].y(), &2); + /// /// # Ok(()) /// # } - /// + /// /// ``` pub fn move_item( &mut self, @@ -552,11 +621,21 @@ impl GridEngine { } } - self.events.trigger_changes_event(&ChangesEventValue { - changes: changes.iter().map(|change| change.clone()).collect(), - }); + self.events.trigger_changes_event(&ChangesEventValue::new( + changes.iter().map(|change| change.clone()).collect(), + )); Ok(()) } + + /// Returns a reference to the grid events system. + pub fn events(&self) -> &GridEvents { + &self.events + } + + /// Returns a mutable reference to the grid events system. + pub fn events_mut(&mut self) -> &mut GridEvents { + &mut self.events + } } mod tests { diff --git a/src/grid_events.rs b/src/grid_events.rs index 0052055..71c1926 100644 --- a/src/grid_events.rs +++ b/src/grid_events.rs @@ -32,8 +32,8 @@ //! let mut grid = GridEngine::new(10, 10); //! //! // Add a listener to track changes -//! grid.events.add_changes_listener(Box::new(|event| { -//! println!("Grid changed: {:?}", event.changes); +//! grid.events_mut().add_changes_listener(Box::new(|event| { +//! println!("Grid changed: {:?}", event.changes()); //! })); //! //! // Make changes to the grid @@ -51,7 +51,23 @@ use std::fmt::Debug; #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct ChangesEventValue { /// Vector of changes that occurred in the grid - pub changes: Vec, + changes: Vec, +} + +impl ChangesEventValue { + /// Creates a new `ChangesEventValue` instance with the specified changes. + /// + /// # Arguments + /// + /// * `changes` - A vector of changes that occurred in the grid + pub fn new(changes: Vec) -> Self { + Self { changes } + } + + /// Returns a reference to the changes vector. + pub fn changes(&self) -> &Vec { + &self.changes + } } /// Type alias for change event listener functions. @@ -66,9 +82,21 @@ pub type ChangesEventFn = Box () + Send + 'static /// callback function to be executed when changes occur. pub struct ListenerFunction { /// Unique identifier for the listener - pub id: String, + id: String, /// The callback function to execute when changes occur - pub function: ChangesEventFn, + function: ChangesEventFn, +} + +impl ListenerFunction { + /// Creates a new `ListenerFunction` with the specified ID and function. + /// + /// # Arguments + /// + /// * `id` - Unique identifier for the listener + /// * `function` - The callback function to execute when changes occur + pub fn new(id: String, function: ChangesEventFn) -> Self { + Self { id, function } + } } impl Debug for ListenerFunction { @@ -111,16 +139,13 @@ impl GridEvents { /// use grid_engine::grid_engine::GridEngine; /// /// let mut grid = GridEngine::new(10, 10); - /// let listener_id = grid.events.add_changes_listener(Box::new(|event| { - /// println!("Changes occurred: {:?}", event.changes); + /// let listener_id = grid.events_mut().add_changes_listener(Box::new(|event| { + /// println!("Changes occurred: {:?}", event.changes()); /// })); /// ``` pub fn add_changes_listener(&mut self, function: ChangesEventFn) -> String { let id = uuid::Uuid::new_v4().to_string(); - let listener = ListenerFunction { - id: id.clone(), - function, - }; + let listener = ListenerFunction::new(id.clone(), function); self.changes_listeners.push(listener); id @@ -138,8 +163,8 @@ impl GridEvents { /// use grid_engine::grid_engine::GridEngine; /// /// let mut grid = GridEngine::new(10, 10); - /// let listener_id = grid.events.add_changes_listener(Box::new(|_| {})); - /// let removed = grid.events.remove_changes_listener(&listener_id); // Listener removed + /// let listener_id = grid.events_mut().add_changes_listener(Box::new(|_| {})); + /// let removed = grid.events_mut().remove_changes_listener(&listener_id); // Listener removed /// /// assert!(removed.is_some()); /// @@ -263,7 +288,7 @@ mod tests { // Create a mock change let node = crate::node::Node::new("test".to_string(), 0, 0, 1, 1); - let change = Change::Add(crate::grid_engine::AddChangeData { value: node }); + let change = Change::Add(crate::grid_engine::AddChangeData::new(node)); let event = ChangesEventValue { changes: vec![change.clone()], }; diff --git a/src/inner_grid.rs b/src/inner_grid.rs index f9646c7..4a65fe7 100644 --- a/src/inner_grid.rs +++ b/src/inner_grid.rs @@ -28,9 +28,9 @@ //! The grid automatically expands vertically when needed, allowing for //! flexible layout management while maintaining horizontal constraints. +use crate::{error::InnerGridError, node::Node}; use grid::Grid; use std::ops::{Deref, DerefMut}; -use crate::{error::InnerGridError, node::Node}; /// Operation to perform when updating the grid. #[derive(Debug, Clone, Copy)] @@ -174,10 +174,10 @@ impl InnerGrid { match operation { UpdateGridOperation::Add => { - *cell = Some(node.id.clone()); + *cell = Some(node.id().to_string()); } UpdateGridOperation::Remove => { - if cell.as_ref() == Some(&node.id) { + if cell.as_ref() == Some(&node.id().to_string()) { *cell = None; } } diff --git a/src/node.rs b/src/node.rs index b4caa7a..0ee200b 100644 --- a/src/node.rs +++ b/src/node.rs @@ -27,7 +27,7 @@ use crate::{ error::InnerGridError, inner_grid::{InnerGrid, UpdateGridOperation}, - utils::{for_cell, ForCellArgs}, + utils::{ForCellArgs, for_cell}, }; /// Represents an item in the grid with position and dimensions. @@ -42,15 +42,15 @@ use crate::{ #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Node { /// Unique identifier for the node - pub id: String, + pub(crate) id: String, /// X coordinate of the top-left corner - pub x: usize, + pub(crate) x: usize, /// Y coordinate of the top-left corner - pub y: usize, + pub(crate) y: usize, /// Width of the node in grid cells - pub w: usize, + pub(crate) w: usize, /// Height of the node in grid cells - pub h: usize, + pub(crate) h: usize, } impl Node { @@ -121,6 +121,31 @@ impl Node { Ok(()) } + + /// Returns the unique identifier of the node. + pub fn id(&self) -> &str { + &self.id + } + + /// Returns the x coordinate of the node. + pub fn x(&self) -> &usize { + &self.x + } + + /// Returns the y coordinate of the node. + pub fn y(&self) -> &usize { + &self.y + } + + /// Returns the width of the node. + pub fn w(&self) -> &usize { + &self.w + } + + /// Returns the height of the node. + pub fn h(&self) -> &usize { + &self.h + } } #[cfg(test)] From ee4f60471e1620c73fd3a451ba98d02a26576534 Mon Sep 17 00:00:00 2001 From: Thiago Ramos <99thiagodejesus@gmail.com> Date: Wed, 21 May 2025 21:08:29 -0300 Subject: [PATCH 4/5] refactor: adjust clippy issue --- src/grid_engine.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/grid_engine.rs b/src/grid_engine.rs index 170707e..f1e0e56 100644 --- a/src/grid_engine.rs +++ b/src/grid_engine.rs @@ -620,9 +620,8 @@ impl GridEngine { } } - self.events.trigger_changes_event(&ChangesEventValue::new( - changes.iter().map(|change| change.clone()).collect(), - )); + self.events + .trigger_changes_event(&ChangesEventValue::new(changes.iter().cloned().collect())); Ok(()) } From 1be01574c895ef55a5579ef643027a3e86f44569 Mon Sep 17 00:00:00 2001 From: Thiago Ramos <99thiagodejesus@gmail.com> Date: Wed, 21 May 2025 21:17:59 -0300 Subject: [PATCH 5/5] refactor: adjust clippy issue --- src/grid_engine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/grid_engine.rs b/src/grid_engine.rs index f1e0e56..5e26aa0 100644 --- a/src/grid_engine.rs +++ b/src/grid_engine.rs @@ -621,7 +621,7 @@ impl GridEngine { } self.events - .trigger_changes_event(&ChangesEventValue::new(changes.iter().cloned().collect())); + .trigger_changes_event(&ChangesEventValue::new(changes.to_vec())); Ok(()) }