Fixed parsing of opening hours with a discount. Fixes #4
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+39
-6
@@ -309,7 +309,19 @@ impl TryFrom<&str> for DayHour {
|
|||||||
return Err(AppError::HourParseError)
|
return Err(AppError::HourParseError)
|
||||||
}
|
}
|
||||||
|
|
||||||
let times = value.split("-")
|
// Optional discount in parentheses after the interval: "08:00 - 12:00 (10)"
|
||||||
|
let (interval, discount) = match value.trim().strip_suffix(")") {
|
||||||
|
Some(rest) => {
|
||||||
|
let (interval, discount) = rest.rsplit_once("(").ok_or(AppError::HourParseError)?;
|
||||||
|
if discount.is_empty() || !discount.chars().all(|c| c.is_ascii_digit()) {
|
||||||
|
return Err(AppError::HourParseError)
|
||||||
|
}
|
||||||
|
(interval, Some(discount.parse::<i32>().map_err(|_| AppError::HourParseError)?))
|
||||||
|
}
|
||||||
|
None => (value, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let times = interval.split("-")
|
||||||
.map(|t| t.trim()).collect::<Vec<_>>();
|
.map(|t| t.trim()).collect::<Vec<_>>();
|
||||||
|
|
||||||
if times.len() != 2 {
|
if times.len() != 2 {
|
||||||
@@ -326,7 +338,7 @@ impl TryFrom<&str> for DayHour {
|
|||||||
Ok(DayHour {
|
Ok(DayHour {
|
||||||
from: from.unwrap(),
|
from: from.unwrap(),
|
||||||
to: to.unwrap(),
|
to: to.unwrap(),
|
||||||
discount: None
|
discount
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -892,6 +904,24 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn day_hour_parses_discount() {
|
||||||
|
let expected = DayHour::new(time(8, 0), time(12, 30), Some(10));
|
||||||
|
|
||||||
|
assert_eq!(DayHour::try_from("08:00 - 12:30 (10)"), Ok(expected.clone()));
|
||||||
|
assert_eq!(DayHour::try_from("08:00-12:30(10)"), Ok(expected));
|
||||||
|
assert_eq!(DayHour::try_from("08:00 - 12:30 (0)"), Ok(DayHour::new(time(8, 0), time(12, 30), Some(0))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn day_hour_rejects_malformed_discount() {
|
||||||
|
for input in ["08:00 - 12:00 ()", "08:00 - 12:00 (x)", "08:00 - 12:00 (-5)", "08:00 - 12:00 (1.5)", "08:00 - 12:00 (10",
|
||||||
|
"08:00 - 12:00 10)", "(10) 08:00 - 12:00", "08:00 (10) - 12:00", "08:00 - 12:00 (10) (20)", "(10)",
|
||||||
|
"08:00 - 12:00 (99999999999)"] {
|
||||||
|
assert_eq!(DayHour::try_from(input), Err(AppError::HourParseError), "input: {:?}", input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn day_hours_empty_string_is_closed_day() {
|
fn day_hours_empty_string_is_closed_day() {
|
||||||
let hours = DayHours::try_new("").unwrap();
|
let hours = DayHours::try_new("").unwrap();
|
||||||
@@ -930,7 +960,8 @@ mod tests {
|
|||||||
fn day_hours_display_round_trip() {
|
fn day_hours_display_round_trip() {
|
||||||
let hours = DayHours(vec![
|
let hours = DayHours(vec![
|
||||||
DayHour::new(time(8, 0), time(12, 0), None),
|
DayHour::new(time(8, 0), time(12, 0), None),
|
||||||
DayHour::new(time(13, 0), time(17, 0), None)
|
DayHour::new(time(13, 0), time(17, 0), Some(10)),
|
||||||
|
DayHour::new(time(18, 0), time(20, 0), Some(0))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let parsed = DayHours::try_new(&hours.to_string()).unwrap();
|
let parsed = DayHours::try_new(&hours.to_string()).unwrap();
|
||||||
@@ -950,9 +981,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn week_hours_accepts_valid_hours() {
|
fn week_hours_accepts_valid_hours() {
|
||||||
for input in ["", "08:00 - 12:00", "08:00-12:00", "08:00 - 12:00, 13:00 - 17:00"] {
|
for input in ["", "08:00 - 12:00", "08:00-12:00", "08:00 - 12:00, 13:00 - 17:00",
|
||||||
|
"08:00 - 12:00 (10)", "08:00 - 12:00 (10), 13:00 - 17:00", "08:00 - 12:00, 13:00 - 17:00(20)"] {
|
||||||
let hours = WeekHours { day: Weekday::Mon, hours: input.to_string() };
|
let hours = WeekHours { day: Weekday::Mon, hours: input.to_string() };
|
||||||
assert!(hours.validate().is_ok(), "input: {:?}", input);
|
assert!(hours.validate().is_ok(), "input: {:?}", input);
|
||||||
|
assert!(DayHours::try_new(input).is_ok(), "input: {:?}", input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -978,7 +1011,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn week_hours_validation_agrees_with_parser() {
|
fn week_hours_validation_agrees_with_parser() {
|
||||||
for input in ["", "08:00 - 12:00", "08:00-12:00", "08:00 - 12:00,13:00 - 17:00", "08:00 - 12:00 , 13:00 - 17:00",
|
for input in ["", "08:00 - 12:00", "08:00-12:00", "08:00 - 12:00,13:00 - 17:00", "08:00 - 12:00 , 13:00 - 17:00",
|
||||||
"08:00 - 12:00, nonsense", "08:00 - 12:00,", "25:00 - 26:00", "08:00 - 12:00 garbage"] {
|
"08:00 - 12:00 (10)", "08:00 - 12:00 (10) , 13:00 - 17:00 (5)", "08:00 - 12:00 (99999999999)", "08:00 - 12:00, nonsense", "08:00 - 12:00,", "25:00 - 26:00", "08:00 - 12:00 garbage"] {
|
||||||
let hours = WeekHours { day: Weekday::Mon, hours: input.to_string() };
|
let hours = WeekHours { day: Weekday::Mon, hours: input.to_string() };
|
||||||
if hours.validate().is_ok() {
|
if hours.validate().is_ok() {
|
||||||
assert!(DayHours::try_new(input).is_ok(), "input: {:?}", input);
|
assert!(DayHours::try_new(input).is_ok(), "input: {:?}", input);
|
||||||
@@ -1003,7 +1036,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn week_hours_rejects_to_before_from() {
|
fn week_hours_rejects_to_before_from() {
|
||||||
for input in ["12:00 - 08:00", "08:00 - 08:00", "08:00 - 12:00, 17:00 - 13:00"] {
|
for input in ["12:00 - 08:00", "08:00 - 08:00", "08:00 - 12:00, 17:00 - 13:00", "12:00 - 08:00 (10)"] {
|
||||||
let hours = WeekHours { day: Weekday::Mon, hours: input.to_string() };
|
let hours = WeekHours { day: Weekday::Mon, hours: input.to_string() };
|
||||||
assert!(has_field_error(&hours, "__all__"), "input: {:?}", input);
|
assert!(has_field_error(&hours, "__all__"), "input: {:?}", input);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user