soliterm-model 0.1.0

Shared model types for soliterm
Documentation
use super::FromTo;
use crate::zones::{stock, waste};
use crate::{action, undo};

// TODO: Move this somewhere better.
#[derive(Debug, Clone, Copy)]
pub struct DrawCount(pub usize);

#[derive(Debug)]
pub struct Action;

#[derive(Debug)]
pub struct Value(Undo);

#[derive(Debug)]
enum Undo {
    Draw(stock::draw::Value, waste::place::Value),
    Replace(waste::empty::Value, stock::replace::Value),
}

impl action::Action for Action {
    type State<'s> = &'s DrawCount;
    type Value = Value;
    type Error = !;
}

impl<'a, S, W> action::Target<Action> for FromTo<'a, S, W>
where
    S: action::Target<stock::draw::Action> + action::Target<stock::replace::Action>,
    W: action::Target<waste::empty::Action> + action::Target<waste::place::Action>,
{
    fn update(&mut self, _: Action, DrawCount(draw_count): &DrawCount) -> Result<Value, !> {
        let FromTo(stock, waste) = self;

        // Try to draw first, and if that fails then try to replace.
        match stock.update(stock::draw::Action(*draw_count), ()) {
            Ok(draw_value) => {
                let to_place = draw_value.drawn().clone();
                let Ok(place_value) = waste.update(waste::place::Action(to_place), ());

                Ok(Value(Undo::Draw(draw_value, place_value)))
            }
            Err(stock::draw::Error::Empty) => {
                let Ok(empty_value) = waste.update(waste::empty::Action, ());
                let to_replace = empty_value.emptied().clone();

                match stock.update(stock::replace::Action(to_replace), ()) {
                    Ok(replace_value) => Ok(Value(Undo::Replace(empty_value, replace_value))),
                    Err(stock::replace::Error::NotEmpty) => {
                        panic!("Call Shroedinger: The stock is both empty and not empty!")
                    }
                }
            }
        }
    }
}

impl<'a, S, W> undo::Target<Value> for FromTo<'a, S, W>
where
    S: undo::Target<stock::draw::Value> + undo::Target<stock::replace::Value>,
    W: undo::Target<waste::empty::Value> + undo::Target<waste::place::Value>,
{
    fn revert(&mut self, Value(undo): Value) {
        let FromTo(stock, waste) = self;

        match undo {
            Undo::Draw(draw_value, place_value) => {
                waste.revert(place_value);
                stock.revert(draw_value);
            }
            Undo::Replace(empty_value, replace_value) => {
                stock.revert(replace_value);
                waste.revert(empty_value);
            }
        }
    }
}