headless_lms_models/library/credit_registration/
classification.rs1use utoipa::ToSchema;
6
7use crate::credit_registrations::CreditRegistrationErrorCode;
8use crate::prelude::*;
9
10#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, Hash, ToSchema)]
13#[serde(rename_all = "snake_case")]
14pub enum Retryability {
15 RetryableTransient,
16 VerifyOnly,
18 PermanentNeedsStudent,
19 PermanentNeedsAdmin,
20 PermanentNeedsConfig,
21}
22
23pub fn retryability(code: CreditRegistrationErrorCode) -> Retryability {
25 use CreditRegistrationErrorCode as Code;
26 use Retryability as Class;
27 match code {
28 Code::ServiceTemporarilyUnavailable | Code::TransportError => Class::RetryableTransient,
29 Code::NotRegistered => Class::RetryableTransient,
31 Code::Unauthorized | Code::MalformedRequest => Class::RetryableTransient,
33 Code::UnexpectedResponse => Class::RetryableTransient,
35 Code::SisuTimeout => Class::VerifyOnly,
36 Code::PersonNotFound
37 | Code::EnrolmentNotFound
38 | Code::EnrolmentNotAccepted
39 | Code::StudyRightNotValid => Class::PermanentNeedsStudent,
40 Code::CourseCodeNotFound
41 | Code::CourseNotAllowed
42 | Code::InvalidGradeForGradeScale
43 | Code::GradeScaleMismatch
44 | Code::InvalidCredits
45 | Code::NoGradeScaleMapping
46 | Code::MissingUhCourseCode
47 | Code::MissingEctsCredits => Class::PermanentNeedsConfig,
48 Code::SisuValidationFailed
49 | Code::Misregistered
50 | Code::RetryWindowExpired
51 | Code::Unknown => Class::PermanentNeedsAdmin,
52 }
53}
54
55pub fn is_repeatable_rejection(code: CreditRegistrationErrorCode) -> bool {
58 use CreditRegistrationErrorCode as Code;
59 match retryability(code) {
60 Retryability::PermanentNeedsStudent | Retryability::PermanentNeedsConfig => true,
61 Retryability::PermanentNeedsAdmin => code == Code::SisuValidationFailed,
63 Retryability::RetryableTransient | Retryability::VerifyOnly => false,
64 }
65}
66
67pub fn is_enrolment_error(code: CreditRegistrationErrorCode) -> bool {
69 matches!(
70 code,
71 CreditRegistrationErrorCode::EnrolmentNotFound
72 | CreditRegistrationErrorCode::EnrolmentNotAccepted
73 )
74}
75
76pub fn is_waiting_error(code: CreditRegistrationErrorCode) -> bool {
79 code == CreditRegistrationErrorCode::NotRegistered || is_enrolment_error(code)
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use CreditRegistrationErrorCode as Code;
86 use Retryability as Class;
87
88 #[test]
91 fn retryability_matches_the_documented_class_for_every_code() {
92 let cases = [
93 (
94 Code::ServiceTemporarilyUnavailable,
95 Class::RetryableTransient,
96 ),
97 (Code::NotRegistered, Class::RetryableTransient),
98 (Code::TransportError, Class::RetryableTransient),
99 (Code::Unauthorized, Class::RetryableTransient),
100 (Code::MalformedRequest, Class::RetryableTransient),
101 (Code::UnexpectedResponse, Class::RetryableTransient),
102 (Code::SisuTimeout, Class::VerifyOnly),
103 (Code::PersonNotFound, Class::PermanentNeedsStudent),
104 (Code::EnrolmentNotFound, Class::PermanentNeedsStudent),
105 (Code::EnrolmentNotAccepted, Class::PermanentNeedsStudent),
106 (Code::StudyRightNotValid, Class::PermanentNeedsStudent),
107 (Code::CourseCodeNotFound, Class::PermanentNeedsConfig),
108 (Code::CourseNotAllowed, Class::PermanentNeedsConfig),
109 (Code::InvalidGradeForGradeScale, Class::PermanentNeedsConfig),
110 (Code::GradeScaleMismatch, Class::PermanentNeedsConfig),
111 (Code::InvalidCredits, Class::PermanentNeedsConfig),
112 (Code::NoGradeScaleMapping, Class::PermanentNeedsConfig),
113 (Code::MissingUhCourseCode, Class::PermanentNeedsConfig),
114 (Code::MissingEctsCredits, Class::PermanentNeedsConfig),
115 (Code::SisuValidationFailed, Class::PermanentNeedsAdmin),
116 (Code::Misregistered, Class::PermanentNeedsAdmin),
117 (Code::RetryWindowExpired, Class::PermanentNeedsAdmin),
118 (Code::Unknown, Class::PermanentNeedsAdmin),
119 ];
120 assert_eq!(
121 cases.len(),
122 CreditRegistrationErrorCode::ALL.len(),
123 "every code must be covered"
124 );
125 for (code, expected) in cases {
126 assert_eq!(retryability(code), expected, "{code:?}");
127 }
128 }
129}