ubs_lib/
ids.rs

1//! Mappings of course/semester/career to internal ids.
2
3use std::str::FromStr;
4
5use thiserror::Error;
6
7/// An enum of available courses in the catalog.
8///
9/// If a course is missing, manually specify its id with [`Course::Raw`](Course::Raw) and
10/// consider sending a PR adding that mapping.
11#[derive(Debug, Clone)]
12pub enum Course {
13    Cse115,
14    Raw(String),
15}
16
17/// An enum of available semesters in the catalog.
18///
19/// If a semester is missing, manually specify its id with [`Semester::Raw`](Semester::Raw) and
20/// consider sending a PR adding that mapping.
21#[derive(Debug, Clone)]
22pub enum Semester {
23    Spring2023,
24    Summer2023,
25    Fall2023,
26    Winter2023,
27    Raw(String),
28}
29
30/// An enum of available careers in the catalog.
31///
32/// If a career is missing, manually specify its id with [`Career::Raw`](Career::Raw) and
33/// consider sending a PR adding that mapping.
34///
35/// Specifying the career is an internal implementation detail exposed by the backend
36/// network API. It doesn't make much sense to have, but nevertheless, it is required.
37#[derive(Debug, Clone)]
38pub enum Career {
39    Undergraduate,
40    Graduate,
41    Law,
42    DentalMedicine,
43    Medicine,
44    Pharmacy,
45    Raw(String),
46}
47
48impl Course {
49    /// Infer the career from the course.
50    ///
51    /// Note that this isn't always possible because a mapping does not yet exist. In
52    /// that case, consider sending a PR adding the mapping.
53    pub fn career(&self) -> Option<Career> {
54        match self {
55            Course::Cse115 => Some(Career::Undergraduate),
56            // in this case it's highly dependent on the course to determine the career
57            Course::Raw(_) => None,
58        }
59    }
60
61    /// Internal id of the course.
62    pub(crate) fn id(&self) -> &str {
63        match self {
64            Course::Cse115 => "004544",
65            Course::Raw(id) => id,
66        }
67    }
68}
69
70impl FromStr for Course {
71    type Err = ParseIdError;
72
73    fn from_str(s: &str) -> Result<Self, Self::Err> {
74        match &*normalize(s) {
75            "CSE115" => Ok(Course::Cse115),
76            // TODO: valid course id is 6 characters and an integer
77            _ => Err(ParseIdError::InvalidId {
78                id: "Course".to_owned(),
79                given: s.to_owned(),
80            }),
81        }
82    }
83}
84
85impl Semester {
86    /// Internal id of the semester.
87    pub(crate) fn id(&self) -> &str {
88        match self {
89            Semester::Spring2023 => "2231",
90            Semester::Summer2023 => "",
91            Semester::Fall2023 => "",
92            Semester::Winter2023 => "",
93            Semester::Raw(id) => id,
94        }
95    }
96}
97
98impl FromStr for Semester {
99    type Err = ParseIdError;
100
101    fn from_str(s: &str) -> Result<Self, Self::Err> {
102        match &*normalize(s) {
103            "SPRING2023" => Ok(Semester::Spring2023),
104            "SUMMER2023" => Ok(Semester::Summer2023),
105            "FALL2023" => Ok(Semester::Fall2023),
106            "WINTER2023" => Ok(Semester::Winter2023),
107            _ => Err(ParseIdError::InvalidId {
108                id: "Semester".to_owned(),
109                given: s.to_owned(),
110            }),
111        }
112    }
113}
114
115impl Career {
116    /// Internal id of the career.
117    pub(crate) fn id(&self) -> &str {
118        match self {
119            Career::Undergraduate => "UGRD",
120            Career::Graduate => "GRAD",
121            Career::Law => "LAW",
122            Career::DentalMedicine => "SDM",
123            Career::Medicine => "MED",
124            Career::Pharmacy => "PHRM",
125            Career::Raw(career) => career,
126        }
127    }
128}
129
130impl FromStr for Career {
131    type Err = ParseIdError;
132
133    fn from_str(s: &str) -> Result<Self, Self::Err> {
134        match &*normalize(s) {
135            "UNDERGRADUATE" => Ok(Career::Undergraduate),
136            "GRADUATE" => Ok(Career::Graduate),
137            "LAW" => Ok(Career::Law),
138            "DENTALMEDICINE" => Ok(Career::DentalMedicine),
139            "MEDICINE" => Ok(Career::Medicine),
140            "PHARMACY" => Ok(Career::Pharmacy),
141            _ => Err(ParseIdError::InvalidId {
142                id: "Career".to_owned(),
143                given: s.to_owned(),
144            }),
145        }
146    }
147}
148
149/// Normalize the input string for use in [`FromStr`](std::str:FromStr) implementations.
150fn normalize(s: &str) -> String {
151    s.chars()
152        .filter(|c| !c.is_whitespace())
153        .collect::<String>()
154        .to_uppercase()
155}
156
157/// Error when parsing id.
158#[derive(Debug, Error)]
159pub enum ParseIdError {
160    /// Specified id could not be converted to enum.
161    ///
162    /// Considering using the `Raw` variant for specifying raw ids.
163    #[error("`{given}` is an invalid `{id}``")]
164    InvalidId { id: String, given: String },
165}