1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
//! Utils for parsing question paper details
use color_eyre::eyre::eyre;
use duplicate::duplicate_item;
use serde::Serialize;
use crate::env::EnvVars;
#[derive(Clone, Copy)]
/// Represents a semester.
///
/// It can be parsed from a [`String`] using the `.try_from()` function. An error will be returned if the given string has an invalid value.
///
/// This value can be converted back into a [`String`] using the [`From`] trait implementation.
pub enum Semester {
/// Autumn semester, parsed from `autumn`
Autumn,
/// Spring semester, parsed from `spring`
Spring,
/// Unknown/wildcard semester, parsed from an empty string.
///
/// Note that this is different from an invalid value and is used to represent papers for which the semester is not known. An invalid value would be `puppy` or `Hippopotomonstrosesquippedaliophobia` for example.
Unknown,
}
impl TryFrom<&String> for Semester {
type Error = color_eyre::eyre::Error;
fn try_from(value: &String) -> Result<Self, Self::Error> {
if value == "autumn" {
Ok(Semester::Autumn)
} else if value == "spring" {
Ok(Semester::Spring)
} else if value.is_empty() {
Ok(Semester::Unknown)
} else {
Err(eyre!("Error parsing semester: Invalid value."))
}
}
}
impl From<Semester> for String {
fn from(value: Semester) -> Self {
match value {
Semester::Autumn => "autumn".into(),
Semester::Spring => "spring".into(),
Semester::Unknown => "".into(),
}
}
}
#[derive(Clone, Copy)]
/// Represents the exam type of the paper.
///
/// Can be converted to and parsed from a String using the [`From`] and [`TryFrom`] trait implementations.
pub enum Exam {
/// Mid-semester examination, parsed from `midsem`
Midsem,
/// End-semester examination, parsed from `endsem`
Endsem,
/// Class test, parsed from either `ct` or `ct` followed by a number (eg: `ct1` or `ct10`).
///
/// The optional number represents the number of the class test (eg: class test 1 or class test 21). This will be None if the number is not known, parsed from `ct`.
CT(Option<usize>),
/// Unknown class test, parsed from an empty string.
///
/// Note that this is different from an invalid value and is used to represent papers for which the exam is not known. An invalid value would be `catto` or `metakgp` for example.
Unknown,
}
impl TryFrom<&String> for Exam {
type Error = color_eyre::eyre::Error;
fn try_from(value: &String) -> Result<Self, Self::Error> {
if value == "midsem" {
Ok(Exam::Midsem)
} else if value == "endsem" {
Ok(Exam::Endsem)
} else if let Some(stripped) = value.strip_prefix("ct") {
if stripped.is_empty() {
Ok(Exam::CT(None))
} else if let Ok(i) = stripped.parse::<usize>() {
Ok(Exam::CT(Some(i)))
} else {
Err(eyre!("Error parsing exam: Invalid class test number."))
}
} else if value.is_empty() {
Ok(Exam::Unknown)
} else {
Err(eyre!("Error parsing exam: Unknown exam type."))
}
}
}
impl From<Exam> for String {
fn from(value: Exam) -> Self {
match value {
Exam::Midsem => "midsem".into(),
Exam::Endsem => "endsem".into(),
Exam::Unknown => "".into(),
Exam::CT(None) => "ct".into(),
Exam::CT(Some(i)) => format!("ct{}", i),
}
}
}
#[duplicate_item(
ExamSem;
[ Exam ];
[ Semester ];
)]
impl Serialize for ExamSem {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&String::from(*self))
}
}
#[derive(Serialize, Clone)]
/// The fields of a question paper sent from the search endpoint
pub struct SearchQP {
pub id: i32,
pub filelink: String,
pub from_library: bool,
pub course_code: String,
pub course_name: String,
pub year: i32,
pub semester: Semester,
pub exam: Exam,
}
#[derive(Serialize, Clone)]
/// The fields of a question paper sent from the admin dashboard endpoints.
///
/// This includes fields such as `approve_status` and `upload_timestamp` that would only be relevant to the dashboard.
pub struct AdminDashboardQP {
pub id: i32,
pub filelink: String,
pub from_library: bool,
pub course_code: String,
pub course_name: String,
pub year: i32,
pub semester: Semester,
pub exam: Exam,
pub upload_timestamp: String,
pub approve_status: bool,
}
#[duplicate_item(
QP;
[ SearchQP ];
[ AdminDashboardQP ];
)]
impl QP {
/// 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.
pub fn with_url(self, env_vars: &EnvVars) -> Result<Self, color_eyre::eyre::Error> {
Ok(Self {
filelink: env_vars.paths.get_url_from_slug(&self.filelink)?,
..self
})
}
}