Skip to main content

rsynko_memory/
session.rs

1use rsynko_session::*;
2use std::cell::Cell;
3use std::rc::Rc;
4use std::time::Duration;
5
6/// Attends to runs that say what they were authored to say, one thing per pass.
7///
8/// Nothing here happens elsewhere: a run is a count of things left to say, and time passes only
9/// when a scenario says it does, so what attendance means can be read without waiting for it.
10#[derive(Debug, Default)]
11pub struct ReferenceSession {
12    requests: Vec<ReferenceRequest>,
13    clock: Duration,
14}
15
16/// Holds one authored request, and everything it has been told about its run.
17#[derive(Debug)]
18struct ReferenceRequest {
19    id: usize,
20    wanted: Wanted,
21    says: usize,
22    beginnable: bool,
23    attended: bool,
24    told: Vec<Telling>,
25    abandonments: Rc<Cell<usize>>,
26}
27
28/// Carries one authored run: the work it has left, what it has said, and whether it was stopped.
29#[derive(Debug)]
30pub struct ReferenceRun {
31    work: usize,
32    said: Vec<usize>,
33    held: bool,
34    abandoning: bool,
35    abandonments: Rc<Cell<usize>>,
36}
37
38impl ReferenceSession {
39    /// Observes one authored request.
40    fn request(&self, id: usize) -> &ReferenceRequest {
41        self.requests
42            .iter()
43            .find(|request| request.id == id)
44            .expect("an authored request")
45    }
46
47    /// Observes one authored request to tell it something.
48    fn request_mut(&mut self, id: usize) -> &mut ReferenceRequest {
49        self.requests
50            .iter_mut()
51            .find(|request| request.id == id)
52            .expect("an authored request")
53    }
54
55    /// Authors one request, and names it.
56    fn author(&mut self, says: usize, beginnable: bool) -> usize {
57        let id = self.requests.len();
58        self.requests.push(ReferenceRequest {
59            id,
60            wanted: Wanted::Running,
61            says,
62            beginnable,
63            attended: false,
64            told: Vec::new(),
65            abandonments: Rc::new(Cell::new(0)),
66        });
67        id
68    }
69}
70
71impl SessionSorts for ReferenceSession {
72    type Id = usize;
73    type Run = ReferenceRun;
74    type Report = usize;
75    type Ending = ();
76    type Refusal = String;
77}
78
79impl UndertakingAlg for ReferenceSession {
80    fn unattended(&self) -> Vec<usize> {
81        self.requests
82            .iter()
83            .filter(|request| !request.attended)
84            .map(|request| request.id)
85            .collect()
86    }
87
88    fn begin(&self, id: &usize) -> Result<ReferenceRun, String> {
89        let request = self.request(*id);
90        if !request.beginnable {
91            return Err("the work refuses to begin".to_owned());
92        }
93        Ok(ReferenceRun {
94            work: request.says,
95            said: Vec::new(),
96            held: false,
97            abandoning: false,
98            abandonments: Rc::clone(&request.abandonments),
99        })
100    }
101}
102
103impl RunReadAlg for ReferenceSession {
104    fn run_is_over(&self, run: &ReferenceRun) -> bool {
105        run.work == 0
106    }
107
108    fn read_run(&self, run: &mut ReferenceRun) -> Vec<usize> {
109        if run.abandoning {
110            // Being told to stop is honored where the run next looks, which is here.
111            run.work = 0;
112            run.said.clear();
113            return Vec::new();
114        }
115        if run.held {
116            return Vec::new();
117        }
118        // What a run says reaches a reader after it was said, so the last thing it says is still
119        // waiting to be read once the run itself is over.
120        let said = std::mem::take(&mut run.said);
121        if run.work > 0 {
122            run.work -= 1;
123            run.said.push(run.work);
124        }
125        said
126    }
127
128    fn end_run(&self, _run: ReferenceRun) -> Result<(), String> {
129        Ok(())
130    }
131}
132
133impl RunHoldAlg for ReferenceSession {
134    fn holding_is_possible(&self) -> bool {
135        true
136    }
137
138    fn hold_run(&self, run: &mut ReferenceRun, held: bool) {
139        run.held = held;
140    }
141
142    fn abandon_run(&self, run: &mut ReferenceRun) {
143        run.abandoning = true;
144        run.abandonments.set(run.abandonments.get() + 1);
145    }
146}
147
148impl AttentionAlg for ReferenceSession {
149    fn begun(&mut self, id: &usize, _holdable: bool) {
150        let request = self.request_mut(*id);
151        request.attended = true;
152        request.told.push(Telling::Begun);
153    }
154
155    fn heard(&mut self, id: &usize, _report: usize) {
156        self.request_mut(*id).told.push(Telling::Heard);
157    }
158
159    fn ran_for(&mut self, id: &usize, elapsed: Duration) {
160        self.request_mut(*id).told.push(Telling::RanFor(elapsed));
161    }
162
163    fn ended(&mut self, id: &usize, ending: Result<(), String>) {
164        let request = self.request_mut(*id);
165        // Work that refused to begin is over the moment it refused, and is not begun again.
166        request.attended = true;
167        request.told.push(match ending {
168            Ok(()) => Telling::Ended,
169            Err(_) => Telling::Refused,
170        });
171    }
172
173    fn wanted(&self, id: &usize) -> Wanted {
174        self.request(*id).wanted
175    }
176}
177
178impl ClockAlg for ReferenceSession {
179    type Moment = Duration;
180
181    fn now(&self) -> Duration {
182        self.clock
183    }
184
185    fn since(&self, moment: &Duration) -> Duration {
186        self.clock.saturating_sub(*moment)
187    }
188}
189
190impl SessionLawFixture for ReferenceSession {
191    fn law_wanting(&mut self, says: usize) -> usize {
192        self.author(says, true)
193    }
194
195    fn law_unbeginnable(&mut self) -> usize {
196        self.author(0, false)
197    }
198
199    fn law_wants(&mut self, id: &usize, wanted: Wanted) {
200        self.request_mut(*id).wanted = wanted;
201    }
202
203    fn law_passes(&mut self, elapsed: Duration) {
204        self.clock = self.clock.saturating_add(elapsed);
205    }
206
207    fn law_told(&self, id: &usize) -> Vec<Telling> {
208        self.request(*id).told.clone()
209    }
210
211    fn law_abandonments(&self, id: &usize) -> usize {
212        self.request(*id).abandonments.get()
213    }
214}