1use color_eyre::eyre::eyre;
4use duplicate::duplicate_item;
5use serde::Deserialize;
6use serde::Serialize;
7
8use crate::env::EnvVars;
9
10#[derive(Clone, Copy)]
11pub enum Semester {
17 Autumn,
19 Spring,
21 Unknown,
25}
26
27impl TryFrom<&String> for Semester {
28 type Error = color_eyre::eyre::Error;
29
30 fn try_from(value: &String) -> Result<Self, Self::Error> {
31 if value == "autumn" {
32 Ok(Semester::Autumn)
33 } else if value == "spring" {
34 Ok(Semester::Spring)
35 } else if value.is_empty() {
36 Ok(Semester::Unknown)
37 } else {
38 Err(eyre!("Error parsing semester: Invalid value."))
39 }
40 }
41}
42
43impl From<Semester> for String {
44 fn from(value: Semester) -> Self {
45 match value {
46 Semester::Autumn => "autumn".into(),
47 Semester::Spring => "spring".into(),
48 Semester::Unknown => "".into(),
49 }
50 }
51}
52
53#[derive(Clone, Copy)]
54pub enum Exam {
58 Midsem,
60 Endsem,
62 CT(Option<usize>),
66 Unknown,
70}
71
72impl TryFrom<&String> for Exam {
73 type Error = color_eyre::eyre::Error;
74
75 fn try_from(value: &String) -> Result<Self, Self::Error> {
76 if value == "midsem" {
77 Ok(Exam::Midsem)
78 } else if value == "endsem" {
79 Ok(Exam::Endsem)
80 } else if let Some(stripped) = value.strip_prefix("ct") {
81 if stripped.is_empty() {
82 Ok(Exam::CT(None))
83 } else if let Ok(i) = stripped.parse::<usize>() {
84 Ok(Exam::CT(Some(i)))
85 } else {
86 Err(eyre!("Error parsing exam: Invalid class test number."))
87 }
88 } else if value.is_empty() {
89 Ok(Exam::Unknown)
90 } else {
91 Err(eyre!("Error parsing exam: Unknown exam type."))
92 }
93 }
94}
95
96impl From<Exam> for String {
97 fn from(value: Exam) -> Self {
98 match value {
99 Exam::Midsem => "midsem".into(),
100 Exam::Endsem => "endsem".into(),
101 Exam::Unknown => "".into(),
102 Exam::CT(None) => "ct".into(),
103 Exam::CT(Some(i)) => format!("ct{}", i),
104 }
105 }
106}
107
108#[duplicate_item(
109 Serializable;
110 [ Exam ];
111 [ Semester ];
112)]
113impl Serialize for Serializable {
114 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
115 where
116 S: serde::Serializer,
117 {
118 serializer.serialize_str(&String::from(*self))
119 }
120}
121
122pub trait WithUrl: Sized {
123 fn with_url(self, env_vars: &EnvVars) -> Result<Self, color_eyre::eyre::Error>;
125}
126
127#[derive(Deserialize)]
128pub struct LibraryQP {
130 pub course_code: String,
131 pub course_name: String,
132 pub year: i32,
133 pub exam: String,
134 pub semester: String,
135 #[allow(dead_code)]
136 pub filename: String,
137 pub approve_status: bool,
138}
139
140#[derive(Serialize, Clone)]
141pub struct BaseQP {
143 pub id: i32,
144 pub filelink: String,
145 pub from_library: bool,
146 pub course_code: String,
147 pub course_name: String,
148 pub year: i32,
149 pub semester: Semester,
150 pub exam: Exam,
151 pub note: String,
152}
153
154#[derive(Serialize, Clone)]
155pub struct AdminDashboardQP {
159 #[serde(flatten)]
160 pub qp: BaseQP,
161 pub upload_timestamp: String,
162 pub approve_status: bool,
163}
164
165impl WithUrl for BaseQP {
166 fn with_url(self, env_vars: &EnvVars) -> Result<Self, color_eyre::eyre::Error> {
167 Ok(Self {
168 filelink: env_vars.paths.get_url_from_slug(&self.filelink)?,
169 ..self
170 })
171 }
172}
173
174impl WithUrl for AdminDashboardQP {
175 fn with_url(self, env_vars: &EnvVars) -> Result<Self, color_eyre::eyre::Error> {
176 Ok(Self {
177 qp: self.qp.with_url(env_vars)?,
178 ..self
179 })
180 }
181}