Skip to main content

headless_lms_credit_registration/workflow/
claim.rs

1//! A row a phase claimed, and the state its answer's write expects to find it in.
2
3use chrono::{DateTime, Utc};
4use headless_lms_models::credit_registrations::{CreditRegistration, CreditRegistrationState};
5use headless_lms_models::library::credit_registration::outcomes::RowFacts;
6use uuid::Uuid;
7
8/// A claimed row. Every answer and refusal is written against it, and lands only if the row is
9/// still in [`Self::expected_state`]: the decision was made from a snapshot that the request, or
10/// just the gap since the claim, may have let another writer overtake.
11pub(crate) struct ClaimedRegistration {
12    registration: CreditRegistration,
13    expected_state: CreditRegistrationState,
14}
15
16impl ClaimedRegistration {
17    /// The claim moves the row into `in_flight` before the request leaves.
18    pub(crate) fn moved_to(
19        registration: CreditRegistration,
20        in_flight: CreditRegistrationState,
21    ) -> Self {
22        Self {
23            registration,
24            expected_state: in_flight,
25        }
26    }
27
28    /// The claim leaves the row where it stands, such as a verify lease or a parked check.
29    pub(crate) fn left_in_place(registration: CreditRegistration) -> Self {
30        Self {
31            expected_state: registration.state,
32            registration,
33        }
34    }
35
36    /// The row as claimed: its `state` is the one it had before the claim moved it.
37    pub(crate) fn registration(&self) -> &CreditRegistration {
38        &self.registration
39    }
40
41    pub(crate) fn id(&self) -> Uuid {
42        self.registration.id
43    }
44
45    /// The row back, to be claimed again as expecting the state the claim's own move just wrote.
46    pub(crate) fn into_registration(self) -> CreditRegistration {
47        self.registration
48    }
49
50    pub(crate) fn expected_state(&self) -> CreditRegistrationState {
51        self.expected_state
52    }
53
54    pub(crate) fn facts(&self, now: DateTime<Utc>) -> RowFacts {
55        RowFacts::of(&self.registration, now)
56    }
57}
58
59/// A batch row: its claim, and whatever the flow's claim read alongside it, which the answer is
60/// applied with.
61pub(crate) struct Claimed<Extra> {
62    pub claim: ClaimedRegistration,
63    pub extra: Extra,
64}
65
66#[cfg(test)]
67mod tests {
68    use headless_lms_models::credit_registrations::CreditRegistrationState as State;
69
70    use super::*;
71    use crate::test_fixtures::registration;
72
73    #[test]
74    fn a_moving_claim_expects_its_in_flight_state_and_keeps_the_row_as_read() {
75        let claim = ClaimedRegistration::moved_to(
76            registration(State::CheckingEnrolment),
77            State::Submitting,
78        );
79        assert_eq!(claim.expected_state(), State::Submitting);
80        assert_eq!(claim.registration().state, State::CheckingEnrolment);
81    }
82
83    #[test]
84    fn a_claim_in_place_expects_the_state_it_found() {
85        let claim = ClaimedRegistration::left_in_place(registration(State::NoUsableEnrolment));
86        assert_eq!(claim.expected_state(), State::NoUsableEnrolment);
87    }
88}