From f04531c1d983da7babaf2849d41b52e2157c6e95 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Mon, 27 Jun 2022 08:06:14 +0200 Subject: [PATCH] :sparkles: :fire: SendError --- stackline/src/context.rs | 68 +++++++++++++++++++++++------------- stackline/src/signal.rs | 2 +- stackline/src/tile/macros.rs | 2 -- stackline/src/tile/mod.rs | 6 ++-- stackline/tiles/wire.rs | 7 ++-- 5 files changed, 50 insertions(+), 35 deletions(-) diff --git a/stackline/src/context.rs b/stackline/src/context.rs index 057aee2..579cbfe 100644 --- a/stackline/src/context.rs +++ b/stackline/src/context.rs @@ -45,14 +45,10 @@ use std::ptr::NonNull; // Update the internal state self.0 += 1; - // Send the signal along: first, get the offset (Δx, Δy) associated with its direction and the tile at (x+Δx,y+Δy). - // Note that the next three lines can be shortened to `ctx.accepts_direction(signal.direction())` - if let Some((pos, tile)) = ctx.get_offset(signal.direction().into_offset()) { - // Then, check that `tile` accepts signals - if tile.accepts_signal(signal.direction()) { - // Finally, send the signal - ctx.send(pos, signal).unwrap_or_else(|| unreachable!()); - } + // Send the signal along: first, get the offset (Δx, Δy) associated with its direction and the position at (x+Δx,y+Δy). + if let Some(pos) = ctx.offset(signal.direction().into_offset()) { + // Finally, send the signal + let _ = ctx.send(pos, signal.direction(), signal); } } } @@ -315,8 +311,8 @@ impl<'a> UpdateContext<'a> { /// # impl Tile for MyTile { /// fn update<'b>(&'b mut self, mut ctx: UpdateContext<'b>) { /// if let Some(signal) = ctx.take_signal() { - /// if let Some(pos) = ctx.accepts_direction(Direction::Down) { - /// ctx.send(pos, signal); + /// if let Some(pos) = ctx.offset(Direction::Down.into_offset()) { + /// let _ = ctx.send(pos, Direction::Down, signal); /// } /// } /// } @@ -337,51 +333,57 @@ impl<'a> UpdateContext<'a> { /// /// Returns Some(()) if the signal was stored in a cell, None otherwise. /// The target cell's state will be set to `Active` if it received the signal. - /// The signal's `position` will be set to `pos`. + /// The signal's `position` will be set to `position`. + /// + /// You should change the `direction` of the signal before calling this function. /// /// # Note /// /// The actions of this function will only be executed *after* all the tiles of the [`Pane`] were [`updated`](Pane::step). /// See [`keep`](UpdateContext::keep) for a variant of this method that takes effect immediately. #[ensures( - !self.in_bounds(position) -> ret.is_none(), + !self.in_bounds(position) -> ret.is_err(), "Should return None if position is out of bounds" )] #[ensures( - ret.is_some() -> self.commit.signals.iter().find(|(x, y, _)| position == (*x, *y)).is_some(), + ret.is_ok() -> self.commit.signals.iter().find(|(x, y, _)| position == (*x, *y)).is_some(), "Should add an entry in self.commit.signals if result is Some" )] - pub fn force_send(&mut self, position: (usize, usize), mut signal: Signal) -> Option<()> { - signal.set_position(position); - + pub fn force_send(&mut self, position: (usize, usize), mut signal: Signal) -> Result<(), SendError> { if !self.in_bounds(position) { - return None; + return Err(SendError(signal)); } + signal.set_position(position); + self.commit.send(position, signal); - Some(()) + Ok(()) } - /// Sends a signal to `position` if there is a tile at `position` that will accept our signal + /// Sends a signal to `position` if there is a tile at `position` that will accept our signal. + /// Sets the signal direction to `direction` and its position to `position`. /// /// # Note /// /// The actions of this function will only be executed *after* all the tiles of the [`Pane`] were [`updated`](Pane::step). /// See [`keep`](UpdateContext::keep) for a variant of this method that takes effect immediately. #[ensures( - !self.in_bounds(position) -> ret.is_none(), + !self.in_bounds(position) -> ret.is_err(), "Should return None if position is out of bounds" )] #[ensures( - ret.is_some() -> self.commit.signals.iter().find(|(x, y, _)| position == (*x, *y)).is_some(), + ret.is_ok() -> self.commit.signals.iter().find(|(x, y, _)| position == (*x, *y)).is_some(), "Should add an entry in self.commit.signals if result is Some" )] - pub fn send(&mut self, position: (usize, usize), signal: Signal) -> Option<()> { - if self.accepts_signal(position, signal.direction()) { - self.force_send(position, signal) + pub fn send(&mut self, position: (usize, usize), direction: Direction, signal: Signal) -> Result<(), SendError> { + if self.accepts_signal(position, direction) { + let original_direction = signal.direction(); + self.force_send(position, signal.moved(direction)).map_err(|e| { + SendError(e.0.moved(original_direction)) + }) } else { - None + Err(SendError(signal)) } } @@ -438,6 +440,22 @@ impl<'a> UpdateContext<'a> { } } +pub struct SendError(pub Signal); + +impl std::fmt::Debug for SendError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt.debug_struct("SendError") + .field("signal", &"Signal {{...}}") + .finish() + } +} + +impl std::fmt::Display for SendError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(fmt, "Couldn't send signal!") + } +} + /// Temporarily holds a list of actions to be made on a given Pane, which should be [applied](UpdateCommit::apply) /// after every tile was updated. pub(crate) struct UpdateCommit { diff --git a/stackline/src/signal.rs b/stackline/src/signal.rs index de1652b..68f10f7 100644 --- a/stackline/src/signal.rs +++ b/stackline/src/signal.rs @@ -98,7 +98,7 @@ impl Signal { /// if let Some(signal) = ctx.take_signal() { /// // We have a signal, see if it can be sent down /// if let Some(pos) = ctx.accepts_direction(direction) { - /// ctx.send(pos, signal.moved(direction)); + /// ctx.send(pos, direction, signal); /// } /// } /// } diff --git a/stackline/src/tile/macros.rs b/stackline/src/tile/macros.rs index 63b3765..3b818ae 100644 --- a/stackline/src/tile/macros.rs +++ b/stackline/src/tile/macros.rs @@ -1,5 +1,3 @@ -use super::*; - #[macro_export] macro_rules! test_tile_setup { ( $width:expr, $height:expr, [ $( $x:expr ),* ] ) => {{ diff --git a/stackline/src/tile/mod.rs b/stackline/src/tile/mod.rs index d1d4089..5cf3635 100644 --- a/stackline/src/tile/mod.rs +++ b/stackline/src/tile/mod.rs @@ -73,9 +73,9 @@ include!(concat!(env!("OUT_DIR"), "/anytile.rs")); /// /// // First, get the coordinates of the tile to our right: /// if let Some(right_position) = context.offset((1, 0)) { -/// // Then, send the signal! -/// // We also need to tell the signal that it is moving to the right. -/// context.send(right_position, signal.moved(Direction::Right)); +/// // Then, send the signal! We also need to tell `send` +/// // that the signal is moving to the right. +/// context.send(right_position, Direction::Right, signal); /// } /// } /// diff --git a/stackline/tiles/wire.rs b/stackline/tiles/wire.rs index a353eca..57db725 100644 --- a/stackline/tiles/wire.rs +++ b/stackline/tiles/wire.rs @@ -1,6 +1,5 @@ //! Wires and diodes -// use super::*; use crate::prelude::*; use crate::tile::prelude::*; @@ -22,7 +21,7 @@ impl Tile for Wire { } if let Some(pos) = context.accepts_direction(direction) { - context.force_send(pos, signal.clone_move(direction)); + context.force_send(pos, signal.clone_move(direction)).unwrap_or_else(|_| unreachable!()); } } } @@ -65,7 +64,7 @@ impl Tile for Diode { } if let Some(pos) = context.offset(self.0.into_offset()) { - context.send(pos, signal.moved(self.0)); + let _ = context.send(pos, self.0, signal); } } @@ -105,7 +104,7 @@ impl Tile for Resistor { fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { if let Some(signal) = std::mem::take(&mut self.signal) { if let Some(pos) = context.offset(self.direction.into_offset()) { - context.send(pos, signal.moved(self.direction)); + let _ = context.send(pos, self.direction, signal); } }