use crate::{timing, GameTime, Run, Segment, TimeSpan};
use core::{num::ParseIntError, result::Result as StdResult};
use snafu::{OptionExt, ResultExt};
#[derive(Debug, snafu::Snafu)]
#[snafu(context(suffix(false)))]
pub enum Error {
Empty,
ExpectedCategoryName,
ExpectedAttemptCount,
ParseAttemptCount {
source: ParseIntError,
},
ExpectedWorldName,
ExpectedWorldTime,
ParseWorldTime {
source: timing::ParseError,
},
}
pub type Result<T> = StdResult<T, Error>;
pub fn parse(source: &str) -> Result<Run> {
let mut lines = source.lines();
let line = lines.next().context(Empty)?;
let mut splits = line.split('|');
let category_name = splits.next().context(ExpectedCategoryName)?;
let mut run = Run::new();
run.set_category_name(
category_name
.strip_prefix('#')
.ok_or(Error::ExpectedCategoryName)?,
);
run.set_attempt_count(
splits
.next()
.context(ExpectedAttemptCount)?
.parse()
.context(ParseAttemptCount)?,
);
let mut total_time = TimeSpan::zero();
let mut next_line = lines.next();
while let Some(line) = next_line {
if line.is_empty() {
break;
}
let mut splits = line.split('|');
let world_name = splits.next().context(ExpectedWorldName)?;
total_time += splits
.next()
.context(ExpectedWorldTime)?
.parse()
.context(ParseWorldTime)?;
next_line = lines.next();
let mut has_acts = false;
while let Some(line) = next_line {
if let Some(segment_name) = line.strip_prefix('*') {
run.push_segment(Segment::new(segment_name));
has_acts = true;
next_line = lines.next();
} else {
next_line = Some(line);
break;
}
}
let time = GameTime(Some(total_time)).into();
if has_acts {
run.segments_mut()
.last_mut()
.unwrap()
.set_personal_best_split_time(time);
} else {
let mut segment = Segment::new(world_name);
segment.set_personal_best_split_time(time);
run.push_segment(segment);
}
}
Ok(run)
}