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/Readme.md b/README.md similarity index 100% rename from Readme.md rename to README.md diff --git a/examples/managing_grid.rs b/examples/managing_grid.rs index 319065b..f4555dc 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 ab1a407..5e26aa0 100644 --- a/src/grid_engine.rs +++ b/src/grid_engine.rs @@ -30,17 +30,22 @@ //! //! ``` //! 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}; @@ -54,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 @@ -104,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 { @@ -131,16 +205,36 @@ 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 { Node::new(id, x, y, w, h) } + /// 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 @@ -157,6 +251,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 } @@ -183,9 +291,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, @@ -239,10 +357,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) { @@ -409,10 +533,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, @@ -483,11 +620,20 @@ impl GridEngine { } } - self.events.trigger_changes_event(&ChangesEventValue { - changes: changes.to_vec(), - }); + self.events + .trigger_changes_event(&ChangesEventValue::new(changes.to_vec())); 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 6a12626..a48b09f 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 @@ -48,10 +48,26 @@ 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, + 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 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,11 +163,23 @@ 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(|_| {})); - /// 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()); + /// /// ``` - 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 +191,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); } @@ -243,7 +280,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 2723e06..434f4ea 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, @@ -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/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. diff --git a/src/node.rs b/src/node.rs index 88b8fdd..1c66bce 100644 --- a/src/node.rs +++ b/src/node.rs @@ -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 { @@ -119,6 +119,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)] diff --git a/src/utils.rs b/src/utils.rs index 8be2e1b..5e2d81d 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,