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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use thiserror::Error;
use tokio::sync::watch;

pub const RAISED: bool = true;
pub const LOWERED: bool = false;

/// The gate was dropped, but we still know what value it had before dropping
#[derive(Debug, Error)]
#[error("gate was {0} (where true is RAISED and false is LOWERED) before dropping")]
pub struct BeforeGateDropped(pub bool);

/// The gate was dropped, so raising or lowering it achieves nothing
#[derive(Debug, Error)]
#[error("gate was dropped")]
pub struct GateDropped;

/// The lever was dropped, so waiting any longer for the gate to be raised or lowered can't be meaningful
#[derive(Debug, Error)]
#[error("lever was dropped")]
pub struct LeverDropped;

/// A lever that can [`raise`] and [`lower`] the gate it's associated with
pub struct Lever {
    sender: watch::Sender<bool>,
}

impl Lever {
    /// Raise the gate.
    /// This wakes all tasks waiting on [`Gate::raised`]
    /// (and later calls to it will resolve immediately until the gate is [`lower`]ed).
    pub fn raise(&self) -> Result<(), GateDropped> {
        if self.gate_was_dropped() {
            Err(GateDropped)
        } else {
            self.sender.send_if_modified(|state| {
                if *state == LOWERED {
                    *state = RAISED;
                    true
                } else {
                    false
                }
            });

            Ok(())
        }
    }

    /// Lower the gate.
    /// This wakes all tasks waiting on [`Gate::lowered`]
    /// (and later calls to it will resolve immediately until the gate is [`raise`]d).
    pub fn lower(&self) -> Result<(), GateDropped> {
        if self.gate_was_dropped() {
            Err(GateDropped)
        } else {
            self.sender.send_if_modified(|state| {
                if *state == RAISED {
                    *state = LOWERED;
                    true
                } else {
                    false
                }
            });

            Ok(())
        }
    }

    /// Returns `Ok(true)` if the gate is raised and `Ok(false)` if it's lowered,
    /// or `Err(BeforeGateDropped(true))` if the gate was dropped and was raised before dropping
    /// and `Err(BeforeGateDropped(false))` if the gate was dropped and was lowered before dropping.
    pub fn is_raised(&self) -> Result<bool, BeforeGateDropped> {
        let state = *self.sender.borrow() == RAISED;

        if self.gate_was_dropped() {
            Err(BeforeGateDropped(state))
        } else {
            Ok(state)
        }
    }

    /// Returns `Ok(true)` if the gate is lowered and `Ok(false)` if it's raised,
    /// or `Err(BeforeGateDropped(true))` if the gate was dropped and was lowered before dropping
    /// and `Err(BeforeGateDropped(false))` if the gate was dropped and was raised before dropping.
    pub fn is_lowered(&self) -> Result<bool, BeforeGateDropped> {
        let state = *self.sender.borrow() == LOWERED;

        if self.gate_was_dropped() {
            Err(BeforeGateDropped(state))
        } else {
            Ok(state)
        }
    }

    /// Returns `true` if the gate associated with this lever has been dropped
    /// and `false` if it hasn't.
    pub fn gate_was_dropped(&self) -> bool {
        self.sender.is_closed()
    }
}

/// A gate that can be checked if [`is_raised`] or [`is_lowered`] immediately,
/// or can be waited on to be [`raised`] or [`lowered`].
#[derive(Clone)]
pub struct Gate {
    receiver: watch::Receiver<bool>,
}

impl Gate {
    /// Returns true if the gate (even if the lever has been dropped) is raised and false if it's lowered.
    pub fn is_raised(&self) -> bool {
        *self.receiver.borrow() == RAISED
    }

    /// Returns true if the gate (even if the lever has been dropped) is lowered and false if it's raised.
    pub fn is_lowered(&self) -> bool {
        *self.receiver.borrow() == LOWERED
    }

    /// Wait until the gate is raised
    /// (by a call to [`Lever::raise`])
    pub async fn raised(&mut self) -> Result<(), LeverDropped> {
        match self.receiver.wait_for(|state| *state == RAISED).await {
            Ok(_) => Ok(()),
            Err(_) => Err(LeverDropped),
        }
    }

    /// Wait until the gate is lowered
    /// (by a call to [`Lever::lower`])
    pub async fn lowered(&mut self) -> Result<(), LeverDropped> {
        match self.receiver.wait_for(|state| *state == LOWERED).await {
            Ok(_) => Ok(()),
            Err(_) => Err(LeverDropped),
        }
    }

    /// Returns `true` if the lever associated with this gate has been dropped
    /// and `false` if it hasn't.
    pub fn lever_was_dropped(&self) -> bool {
        self.receiver.has_changed().is_err()
    }
}

/// Create a [`Gate`] that is initially raised.
/// The [`Lever`] that it is returned with can raise and lower the gate.
pub fn new_raised() -> (Lever, Gate) {
    let (sender, receiver) = watch::channel(RAISED);

    let lever = Lever { sender };
    let gate = Gate { receiver };

    (lever, gate)
}

/// Create a [`Gate`] that is initially lowered.
/// The [`Lever`] that it is returned with can raise and lower the gate.
pub fn new_lowered() -> (Lever, Gate) {
    let (sender, receiver) = watch::channel(LOWERED);

    let lever = Lever { sender };
    let gate = Gate { receiver };

    (lever, gate)
}