1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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);
            }
        }
    }
}