diff --git a/stackline/Cargo.toml b/stackline/Cargo.toml index 1cf1658..6463d69 100755 --- a/stackline/Cargo.toml +++ b/stackline/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" palette = "0.6" enum_dispatch = "0.3" contracts = { version = "0.6.3", features = ["override_debug"] } -veccell = "0.2" +veccell = "0.2.1" pathfinding = "3.0" [dev-dependencies] diff --git a/stackline/src/pane.rs b/stackline/src/pane.rs index 001b276..f62a558 100644 --- a/stackline/src/pane.rs +++ b/stackline/src/pane.rs @@ -144,7 +144,7 @@ impl Pane { } // TODO: Have a Result instead of an Option - /// Returns an immutable referenec to the [`Tile`] at `position`. + /// Returns an immutable reference to the [`FullTile`] at `position`. /// If `position` is out of bounds, returns `None`. /// /// # Example @@ -166,7 +166,42 @@ impl Pane { return None; } - self.tiles.borrow(position.1 * self.width.get() + position.0) + self.tiles + .borrow(position.1 * self.width.get() + position.0) + } + + /// Returns an immutable reference to the [`Tile`] at `position`. + /// If `position` is out of bounds, returns `None`. + /// If the tile at position isn't an instance of `T`, returns `None`. + /// + /// `T` may be any [`Tile`] that is a member of [`AnyTile`]. + /// + /// # Example + /// + /// ``` + /// use stackline::prelude::*; + /// use stackline::tile::{FullTile, Wire}; + /// + /// let mut pane = Pane::empty(4, 4).unwrap(); + /// + /// pane.set_tile((0, 0), Wire::new(Orientation::Horizontal)); + /// + /// let wire = pane.get_as::((0, 0)).unwrap(); + /// assert_eq!(wire, Wire::new(Orientation::Horizontal)); + /// ``` + // TODO: error types + #[inline] + pub fn get_as<'b, T: Tile>(&'b self, position: (usize, usize)) -> Option> + where + for<'c> &'c AnyTile: TryInto<&'c T>, + { + let tile = self.get(position)?; + VecRef::try_map(tile, |tile| { + tile.get() + .ok_or(()) + .and_then(|anytile: &AnyTile| anytile.try_into().map_err(|_| ())) + }) + .ok() } /// Returns a mutable reference to the [`Tile`] at `position`. @@ -195,7 +230,44 @@ impl Pane { .get_mut(position.1 * self.width.get() + position.0) } - pub(crate) fn borrow_mut<'b>(&'b self, position: (usize, usize)) -> Option> { + /// Returns a mutable reference to the [`Tile`] at `position`. + /// If `position` is out of bounds, returns `None`. + /// If the tile at position isn't an instance of `T`, returns `None`. + /// + /// `T` may be any [`Tile`] that is a member of [`AnyTile`]. + /// + /// # Example + /// + /// ``` + /// use stackline::prelude::*; + /// use stackline::tile::{FullTile, Wire}; + /// + /// let mut pane = Pane::empty(4, 4).unwrap(); + /// + /// pane.set_tile((0, 0), Wire::new(Orientation::Horizontal)); + /// + /// let mut wire = pane.get_mut_as::((0, 0)).unwrap(); + /// assert_eq!(*wire, Wire::new(Orientation::Horizontal)); + /// *wire = Wire::new(Orientation::Vertical); + /// ``` + #[inline] + pub fn get_mut_as<'b, T: Tile>(&'b mut self, position: (usize, usize)) -> Option<&'b mut T> + where + for<'c> &'c mut AnyTile: TryInto<&'c mut T>, + { + let tile = self.get_mut(position)?.get_mut()?; + tile.try_into().ok() + } + + /// Returns a mutable borrow to the [`Tile`] at `position`. + /// + /// This function does not need a mutable reference to `self`, and makes use + /// of [`VecCell`]'s ability to provide interior mutability for one item at a time. + #[inline] + pub(crate) fn borrow_mut<'b>( + &'b self, + position: (usize, usize), + ) -> Option> { if !self.in_bounds(position) { return None; } @@ -204,6 +276,45 @@ impl Pane { .borrow_mut(position.1 * self.width.get() + position.0) } + /// Returns a mutable borrow to the [`Tile`] at `position`. + /// If `position` is out of bounds, returns `None`. + /// If the tile at position isn't an instance of `T`, returns `None`. + /// + /// `T` may be any [`Tile`] that is a member of [`AnyTile`]. + /// + /// This function does not need a mutable reference to `self`, and makes use + /// of [`VecCell`]'s ability to provide interior mutability for one item at a time. + /// + /// # Example + /// + /// ``` + /// use stackline::prelude::*; + /// use stackline::tile::{FullTile, Wire}; + /// + /// let mut pane = Pane::empty(4, 4).unwrap(); + /// + /// pane.set_tile((0, 0), Wire::new(Orientation::Horizontal)); + /// + /// let pane = pane; + /// + /// let mut wire = pane.borrow_mut_as::((0, 0)).unwrap(); + /// assert_eq!(wire, Wire::new(Orientation::Horizontal)); + /// *wire = Wire::new(Orientation::Vertical); + /// ``` + #[inline] + pub fn borrow_mut_as<'b, T: Tile>(&'b self, position: (usize, usize)) -> Option> + where + for<'c> &'c mut AnyTile: TryInto<&'c mut T>, + { + let tile = self.borrow_mut(position)?; + VecRefMut::try_map(tile, |tile| { + tile.get_mut() + .ok_or(()) + .and_then(|anytile: &mut AnyTile| anytile.try_into().map_err(|_| ())) + }) + .ok() + } + /// Sets the tile at `position` to `tile`. `T` must either implement [`Tile`] or be `()`. #[inline] #[ensures(self.in_bounds(position) -> ret.is_some())] @@ -515,7 +626,8 @@ mod test { assert_eq!(surface.get(5 + 2, 3 + 2).unwrap().ch, '+'); for y in 0..9 { for x in 0..9 { - if (x, y) != (5 + 2, 3 + 1) && (x, y) != (5 + 3, 3 + 1) && (x, y) != (5 + 2, 3 + 2) { + if (x, y) != (5 + 2, 3 + 1) && (x, y) != (5 + 3, 3 + 1) && (x, y) != (5 + 2, 3 + 2) + { assert_eq!(surface.get(x, y), Some(TextChar::default())); } } diff --git a/stackline/tiles/transmit.rs b/stackline/tiles/transmit.rs index c5e37cd..1758e9e 100644 --- a/stackline/tiles/transmit.rs +++ b/stackline/tiles/transmit.rs @@ -353,11 +353,11 @@ mod test { for n in 0..2 { world.step(); - // TODO: pane.get_as::(coords) - let sender: VecRef<'_, Sender> = VecRef::map( - world.get_pane("main").unwrap().get((0, 0)).unwrap(), - |tile| tile.get().unwrap().try_into().unwrap(), - ); + let sender: VecRef<'_, Sender> = world + .get_pane("main") + .unwrap() + .get_as::((0, 0)) + .unwrap(); assert!(sender.signals.len() == 1); assert!(sender.signals[0].1 == n); @@ -370,10 +370,11 @@ mod test { world.step(); - let sender: VecRef<'_, Sender> = VecRef::map( - world.get_pane("main").unwrap().get((0, 0)).unwrap(), - |tile| tile.get().unwrap().try_into().unwrap(), - ); + let sender: VecRef<'_, Sender> = world + .get_pane("main") + .unwrap() + .get_as::((0, 0)) + .unwrap(); assert!(sender.signals.len() == 0); @@ -397,11 +398,11 @@ mod test { world.set_pane(String::from("main"), main_pane); world.set_pane(String::from("second"), second_pane); - // TODO: borrow_mut_as::(coords) - let mut tile: VecRefMut = VecRefMut::map( - world.get_pane("main").unwrap().borrow_mut((0, 0)).unwrap(), - |tile| tile.get_mut().unwrap().try_into().unwrap(), - ); + let mut tile = world + .get_pane("main") + .unwrap() + .borrow_mut_as::((0, 0)) + .unwrap(); tile.calculate_path((0, 0), &world); @@ -412,10 +413,11 @@ mod test { world.get_pane_mut("second").unwrap().set_position((2, 2)); - let mut tile: VecRefMut = VecRefMut::map( - world.get_pane("main").unwrap().borrow_mut((0, 0)).unwrap(), - |tile| tile.get_mut().unwrap().try_into().unwrap(), - ); + let mut tile = world + .get_pane("main") + .unwrap() + .borrow_mut_as::((0, 0)) + .unwrap(); tile.calculate_path((0, 0), &world); diff --git a/stackline/tiles/wire.rs b/stackline/tiles/wire.rs index faaa86a..bfae98a 100644 --- a/stackline/tiles/wire.rs +++ b/stackline/tiles/wire.rs @@ -3,7 +3,7 @@ use crate::prelude::*; // use crate::tile::prelude::*; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Wire(Orientation); impl Wire {