headless_lms_server/domain/
credit_registration_phases.rs1use headless_lms_models::credit_registration_phase_state::PhaseRunOutcome;
9use sqlx::PgPool;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub enum CreditRegistrationPhase {
15 Materialize,
16 Preconditions,
17 ResolveEnrolments,
18 Import,
19 Verify,
20 LegacyMirror,
21 StudentNotifications,
22 EnrolmentDiscovery,
23 LinkEmails,
24 ProductTokenRefresh,
25 ConfigValidation,
26 RetentionSweep,
27}
28
29impl CreditRegistrationPhase {
30 pub const ALL: [Self; 12] = [
32 Self::Materialize,
33 Self::Preconditions,
34 Self::ResolveEnrolments,
35 Self::Import,
36 Self::Verify,
37 Self::LegacyMirror,
38 Self::StudentNotifications,
39 Self::EnrolmentDiscovery,
40 Self::LinkEmails,
41 Self::ProductTokenRefresh,
42 Self::ConfigValidation,
43 Self::RetentionSweep,
44 ];
45
46 pub const REGISTRAR_TICK_SEQUENCE: [Self; 5] = [
51 Self::Materialize,
52 Self::Preconditions,
53 Self::ResolveEnrolments,
54 Self::Import,
55 Self::Verify,
56 ];
57
58 pub fn as_str(self) -> &'static str {
59 match self {
60 Self::Materialize => "materialize",
61 Self::Preconditions => "preconditions",
62 Self::ResolveEnrolments => "resolve-enrolments",
63 Self::Import => "import",
64 Self::Verify => "verify",
65 Self::LegacyMirror => "legacy-mirror",
66 Self::StudentNotifications => "student-notifications",
67 Self::EnrolmentDiscovery => "enrolment-discovery",
68 Self::LinkEmails => "link-emails",
69 Self::ProductTokenRefresh => "product-token-refresh",
70 Self::ConfigValidation => "config-validation",
71 Self::RetentionSweep => "retention-sweep",
72 }
73 }
74
75 pub fn from_phase_name(name: &str) -> Option<Self> {
76 Self::ALL.into_iter().find(|phase| phase.as_str() == name)
77 }
78
79 pub fn process_name(self) -> &'static str {
81 match self {
82 Self::Materialize
83 | Self::Preconditions
84 | Self::ResolveEnrolments
85 | Self::Import
86 | Self::Verify
87 | Self::LegacyMirror
88 | Self::StudentNotifications => "credit-registrar",
89 Self::EnrolmentDiscovery
90 | Self::LinkEmails
91 | Self::ProductTokenRefresh
92 | Self::ConfigValidation
93 | Self::RetentionSweep => "suotar-syncer",
94 }
95 }
96}
97
98#[derive(Debug, Clone, PartialEq)]
100pub enum PhaseTick {
101 Ran(PhaseRunOutcome),
102 NotImplemented,
103}
104
105pub async fn run_phase_once(
112 _pool: &PgPool,
113 phase: CreditRegistrationPhase,
114) -> anyhow::Result<PhaseTick> {
115 match phase {
116 CreditRegistrationPhase::Materialize
117 | CreditRegistrationPhase::Preconditions
118 | CreditRegistrationPhase::ResolveEnrolments
119 | CreditRegistrationPhase::Import
120 | CreditRegistrationPhase::Verify
121 | CreditRegistrationPhase::LegacyMirror
122 | CreditRegistrationPhase::StudentNotifications
123 | CreditRegistrationPhase::EnrolmentDiscovery
124 | CreditRegistrationPhase::LinkEmails
125 | CreditRegistrationPhase::ProductTokenRefresh
126 | CreditRegistrationPhase::ConfigValidation
127 | CreditRegistrationPhase::RetentionSweep => Ok(PhaseTick::NotImplemented),
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use headless_lms_models::credit_registration_phase_state::PHASES;
134
135 use super::*;
136
137 #[test]
140 fn phase_names_match_the_seeded_rows() {
141 let from_enum: Vec<&str> = CreditRegistrationPhase::ALL
142 .iter()
143 .map(|phase| phase.as_str())
144 .collect();
145 assert_eq!(from_enum, PHASES);
146 assert_eq!(from_enum.len(), 12);
147 }
148
149 #[test]
150 fn phase_names_round_trip() {
151 for phase in CreditRegistrationPhase::ALL {
152 assert_eq!(
153 CreditRegistrationPhase::from_phase_name(phase.as_str()),
154 Some(phase)
155 );
156 }
157 assert_eq!(
158 CreditRegistrationPhase::from_phase_name("materialise"),
159 None
160 );
161 assert_eq!(CreditRegistrationPhase::from_phase_name(""), None);
162 }
163}