Skip to content

Commit 5a245ef

Browse files
refactor: reduced duplication in db types
1 parent 5a89942 commit 5a245ef

File tree

4 files changed

+45
-56
lines changed

4 files changed

+45
-56
lines changed

backend/src/db/mod.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl Database {
7070
query: &str,
7171
exam_filter: ExamFilter,
7272
exam_filter_str: String,
73-
) -> Result<Vec<qp::SearchQP>, sqlx::Error> {
73+
) -> Result<Vec<qp::BaseQP>, sqlx::Error> {
7474
let (query_sql, use_exam_arg) = queries::get_qp_search_query(exam_filter);
7575
let query = sqlx::query_as(&query_sql).bind(query);
7676

@@ -80,11 +80,11 @@ impl Database {
8080
query
8181
};
8282

83-
let papers: Vec<models::DBSearchQP> = query.fetch_all(&self.connection).await?;
83+
let papers: Vec<models::DBBaseQP> = query.fetch_all(&self.connection).await?;
8484

8585
Ok(papers
8686
.iter()
87-
.map(|qp| qp::SearchQP::from(qp.clone()))
87+
.map(|qp| qp::BaseQP::from(qp.clone()))
8888
.collect())
8989
}
9090

@@ -125,24 +125,24 @@ impl Database {
125125
let current_details = self.get_paper_by_id(id).await?;
126126

127127
// Construct the final values to be inserted into the db
128-
let course_code = course_code.unwrap_or(current_details.course_code);
129-
let course_name = course_name.unwrap_or(current_details.course_name);
130-
let year = year.unwrap_or(current_details.year);
128+
let course_code = course_code.unwrap_or(current_details.qp.course_code);
129+
let course_name = course_name.unwrap_or(current_details.qp.course_name);
130+
let year = year.unwrap_or(current_details.qp.year);
131131
let semester: String = semester
132132
.map(|sem| Semester::try_from(&sem))
133133
.transpose()?
134-
.unwrap_or(current_details.semester)
134+
.unwrap_or(current_details.qp.semester)
135135
.into();
136136
let exam: String = exam
137137
.map(|exam| Exam::try_from(&exam))
138138
.transpose()?
139-
.unwrap_or(current_details.exam)
139+
.unwrap_or(current_details.qp.exam)
140140
.into();
141141
let approve_status = approve_status.unwrap_or(current_details.approve_status);
142142

143143
// Set the new filelink
144-
let old_filelink = current_details.filelink;
145-
let new_filelink = if current_details.from_library {
144+
let old_filelink = current_details.qp.filelink;
145+
let new_filelink = if current_details.qp.from_library {
146146
old_filelink.clone()
147147
} else if approve_status {
148148
env_vars.paths.get_slug(
@@ -186,9 +186,9 @@ impl Database {
186186
Ok((tx, old_filelink, new_qp))
187187
}
188188

189-
/// Adds a new upload paper's details to the database. Sets the `from_library` field to false.
190-
///
191-
/// Returns the database transaction and the id of the uploaded paper
189+
// /// Adds a new upload paper's details to the database. Sets the `from_library` field to false.
190+
// ///
191+
// /// Returns the database transaction and the id of the uploaded paper
192192
// pub async fn add_uploaded_paper<'c>(
193193
// &self,
194194
// file_details:
@@ -276,9 +276,9 @@ impl Database {
276276
}
277277

278278
// /// Updates filelink for an uploaded question paper uploaded using the [crate::db::Database::update_uploaded_filelink] function. Takes the same transaction that the previous function used.
279-
pub async fn update_uploaded_filelink<'c>(
279+
pub async fn update_uploaded_filelink(
280280
&self,
281-
tx: &mut Transaction<'c, Postgres>,
281+
tx: &mut Transaction<'_, Postgres>,
282282
id: i32,
283283
file_link: &str,
284284
) -> Result<(), color_eyre::eyre::Error> {

backend/src/db/models.rs

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use super::qp;
1010
use sqlx::{prelude::FromRow, types::chrono};
1111

1212
#[derive(FromRow, Clone)]
13-
/// The fields of a question paper sent to the search endpoint
14-
pub struct DBSearchQP {
13+
/// Base/common fields of a question paper
14+
pub struct DBBaseQP {
1515
id: i32,
1616
filelink: String,
1717
from_library: bool,
@@ -25,37 +25,24 @@ pub struct DBSearchQP {
2525
#[derive(FromRow, Clone)]
2626
/// The fields of a question paper sent to the admin dashboard endpoint
2727
pub struct DBAdminDashboardQP {
28-
id: i32,
29-
filelink: String,
30-
from_library: bool,
31-
course_code: String,
32-
course_name: String,
33-
year: i32,
34-
semester: String,
35-
exam: String,
28+
#[sqlx(flatten)]
29+
qp: DBBaseQP,
3630
upload_timestamp: chrono::NaiveDateTime,
3731
approve_status: bool,
3832
}
3933

4034
impl From<DBAdminDashboardQP> for qp::AdminDashboardQP {
4135
fn from(value: DBAdminDashboardQP) -> Self {
4236
Self {
43-
id: value.id,
44-
filelink: value.filelink,
45-
from_library: value.from_library,
46-
course_code: value.course_code,
47-
course_name: value.course_name,
48-
year: value.year,
49-
semester: (&value.semester).try_into().unwrap_or(Semester::Unknown),
50-
exam: (&value.exam).try_into().unwrap_or(qp::Exam::Unknown),
37+
qp: value.qp.into(),
5138
upload_timestamp: value.upload_timestamp.to_string(),
5239
approve_status: value.approve_status,
5340
}
5441
}
5542
}
5643

57-
impl From<DBSearchQP> for qp::SearchQP {
58-
fn from(value: DBSearchQP) -> Self {
44+
impl From<DBBaseQP> for qp::BaseQP {
45+
fn from(value: DBBaseQP) -> Self {
5946
Self {
6047
id: value.id,
6148
filelink: value.filelink,

backend/src/qp.rs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,14 @@ impl Serialize for ExamSem {
118118
}
119119
}
120120

121+
pub trait WithUrl: Sized {
122+
/// Returns the question paper with the full static files URL in the `filelink` field instead of just the slug. See the [`crate::pathutils`] module for what a slug is.
123+
fn with_url(self, env_vars: &EnvVars) -> Result<Self, color_eyre::eyre::Error>;
124+
}
125+
121126
#[derive(Serialize, Clone)]
122127
/// The fields of a question paper sent from the search endpoint
123-
pub struct SearchQP {
128+
pub struct BaseQP {
124129
pub id: i32,
125130
pub filelink: String,
126131
pub from_library: bool,
@@ -136,29 +141,26 @@ pub struct SearchQP {
136141
///
137142
/// This includes fields such as `approve_status` and `upload_timestamp` that would only be relevant to the dashboard.
138143
pub struct AdminDashboardQP {
139-
pub id: i32,
140-
pub filelink: String,
141-
pub from_library: bool,
142-
pub course_code: String,
143-
pub course_name: String,
144-
pub year: i32,
145-
pub semester: Semester,
146-
pub exam: Exam,
144+
#[serde(flatten)]
145+
pub qp: BaseQP,
147146
pub upload_timestamp: String,
148147
pub approve_status: bool,
149148
}
150149

151-
#[duplicate_item(
152-
QP;
153-
[ SearchQP ];
154-
[ AdminDashboardQP ];
155-
)]
156-
impl QP {
157-
/// Returns the question paper with the full static files URL in the `filelink` field instead of just the slug. See the [`crate::pathutils`] module for what a slug is.
158-
pub fn with_url(self, env_vars: &EnvVars) -> Result<Self, color_eyre::eyre::Error> {
150+
impl WithUrl for BaseQP {
151+
fn with_url(self, env_vars: &EnvVars) -> Result<Self, color_eyre::eyre::Error> {
159152
Ok(Self {
160153
filelink: env_vars.paths.get_url_from_slug(&self.filelink)?,
161154
..self
162155
})
163156
}
164157
}
158+
159+
impl WithUrl for AdminDashboardQP {
160+
fn with_url(self, env_vars: &EnvVars) -> Result<Self, color_eyre::eyre::Error> {
161+
Ok(Self {
162+
qp: self.qp.with_url(env_vars)?,
163+
..self
164+
})
165+
}
166+
}

backend/src/routing/handlers.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use serde::Deserialize;
2323
use crate::{
2424
auth::{self, Auth},
2525
pathutils::PaperCategory,
26-
qp::{self, AdminDashboardQP},
26+
qp::{self, AdminDashboardQP, WithUrl},
2727
};
2828

2929
use super::{AppError, BackendResponse, RouterState, Status};
@@ -61,7 +61,7 @@ pub async fn get_unapproved(
6161
pub async fn search(
6262
State(state): State<RouterState>,
6363
Query(params): Query<HashMap<String, String>>,
64-
) -> HandlerReturn<Vec<qp::SearchQP>> {
64+
) -> HandlerReturn<Vec<qp::BaseQP>> {
6565
let response = if let Some(query) = params.get("query") {
6666
let exam_query_str = params
6767
.get("exam")
@@ -77,7 +77,7 @@ pub async fn search(
7777
let papers = papers
7878
.iter()
7979
.map(|paper| paper.clone().with_url(&state.env_vars))
80-
.collect::<Result<Vec<qp::SearchQP>, color_eyre::eyre::Error>>()?;
80+
.collect::<Result<Vec<qp::BaseQP>, color_eyre::eyre::Error>>()?;
8181

8282
Ok(BackendResponse::ok(
8383
format!("Successfully fetched {} papers.", papers.len()),
@@ -179,7 +179,7 @@ pub async fn edit(
179179

180180
// Copy the actual file
181181
let old_filepath = state.env_vars.paths.get_path_from_slug(&old_filelink);
182-
let new_filepath = state.env_vars.paths.get_path_from_slug(&new_qp.filelink);
182+
let new_filepath = state.env_vars.paths.get_path_from_slug(&new_qp.qp.filelink);
183183

184184
if old_filepath != new_filepath {
185185
if let Err(e) = fs::copy(old_filepath, new_filepath).await {

0 commit comments

Comments
 (0)