diff options
Diffstat (limited to 'src/parser.rs')
| -rw-r--r-- | src/parser.rs | 102 |
1 files changed, 99 insertions, 3 deletions
diff --git a/src/parser.rs b/src/parser.rs index 87da6be..0f3ef7b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4,10 +4,11 @@ use nom::{ branch::alt, - bytes::complete::{tag, take, take_till, take_while}, + bytes::complete::{tag, take, take_till, take_until, take_while}, character::complete::{char, multispace0}, - combinator::{map_res, rest}, - sequence::{delimited, pair, preceded, separated_pair}, + combinator::{map, map_res, opt, rest}, + number::complete::float, + sequence::{delimited, pair, preceded, separated_pair, tuple}, IResult, }; @@ -101,6 +102,84 @@ pub fn subtitle(input: &str) -> IResult<&str, &str> { preceded(tag("\u{2013} "), rest)(input) } +/// Parses course prerequisites from an input. +/// +/// # Errors +/// +/// This function will return an error if the input is not preceded by (P). +pub fn prerequisites(input: &str) -> IResult<&str, &str> { + map( + preceded( + tag("(P)"), + alt((take_until("(C)"), take_until("(X)"), rest)), + ), + // The data will often end up with leading and trailing spaces. Trimming is the easiest + // way to get rid of these. + str::trim, + )(input) +} + +/// Parses course corequisites from an input. +/// +/// # Errors +/// +/// This function will return an error if the input is not preceded by (C). +pub fn corequisites(input: &str) -> IResult<&str, &str> { + map( + preceded(tag("(C)"), alt((take_until("(X)"), rest))), + // The data will often end up with leading and trailing spaces. Trimming is the easiest + // way to get rid of these. + str::trim, + )(input) +} + +/// Parses course restrictions from an input. +/// +/// # Errors +/// +/// This function will return an error if the input is not preceded by (X). +pub fn restrictions(input: &str) -> IResult<&str, &str> { + map( + preceded(tag("(X)"), rest), + // The data will often end up with leading and trailing spaces. Trimming is the easiest + // way to get rid of these. + str::trim, + )(input) +} + +/// Alias for the return type of the requirements parser. +type RequirementsReturn<'a> = (Option<&'a str>, Option<&'a str>, Option<&'a str>); + +/// Parses course requirements from an input. +/// +/// # Errors +/// +/// This function should not return an error, and errors are to be considered unreachable. +pub fn requirements(input: &str) -> IResult<&str, RequirementsReturn> { + tuple((opt(prerequisites), opt(corequisites), opt(restrictions)))(input) +} + +/// Parses the course points from an input. +/// +/// # Errors +/// +/// This function will return an error if the input provided does not contain a float. +pub fn course_points(input: &str) -> IResult<&str, f32> { + float(input) +} + +/// Parses the entire "coursepoints" section of the course offering. +/// +/// # Errors +/// +/// This function will return an error if the course points cannot be parsed. +pub fn course_offering(input: &str) -> IResult<&str, (f32, Option<RequirementsReturn>)> { + tuple(( + course_points, + opt(preceded(tag(" pts \u{2022} "), requirements)), + ))(input) +} + #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { @@ -168,4 +247,21 @@ mod tests { "Identification, Assessment and Control of Hazards and Risks" ); } + + #[test] + fn prereq_parser() { + let parsed_prereq = prerequisites("(P) LING 123 (C) SOPH 184 (X) SOPH 185").unwrap(); + + assert_eq!(parsed_prereq.1, "LING 123"); + } + + #[test] + fn req_parser() { + let parsed_prereq = requirements("(P) LING 229, LING 228; (C) MATH 883").unwrap(); + + assert_eq!( + parsed_prereq.1, + (Some("LING 229, LING 228;"), Some("MATH 883"), None) + ); + } } |