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