use crate::{timing, RealTime, Run, Segment, Time, TimeSpan};
use alloc::borrow::Cow;
use core::{num::ParseIntError, result::Result as StdResult};
use snafu::{OptionExt, ResultExt};
#[derive(Debug, snafu::Snafu)]
#[snafu(context(suffix(false)))]
pub enum Error {
ExpectedTitle,
ExpectedGoal,
ExpectedAttemptCount,
ParseAttemptCount {
source: ParseIntError,
},
ExpectedSegmentName,
ExpectedSplitTime,
ParseSplitTime {
source: timing::ParseError,
},
ExpectedBestSegmentTime,
ParseBestSegmentTime {
source: timing::ParseError,
},
}
pub type Result<T> = StdResult<T, Error>;
fn parse_time(time: &str) -> StdResult<Time, timing::ParseError> {
let time: TimeSpan = replace(time, ",", ".").parse()?;
if time == TimeSpan::zero() {
Ok(Time::default())
} else {
Ok(RealTime(Some(time)).into())
}
}
fn replace<'text>(text: &'text str, a: &str, b: &str) -> Cow<'text, str> {
if text.contains(a) {
text.replace(a, b).into()
} else {
text.into()
}
}
pub fn parse(source: &str, #[allow(unused)] load_icons: bool) -> Result<Run> {
let mut run = Run::new();
#[cfg(feature = "std")]
let mut icon_buf = Vec::new();
let mut lines = source.lines();
run.set_category_name(lines.next().context(ExpectedTitle)?);
let goal = lines.next().context(ExpectedGoal)?;
if !goal.trim_start().is_empty() {
run.metadata_mut()
.custom_variable_mut("Goal")
.permanent()
.set_value(goal);
}
run.set_attempt_count(
lines
.next()
.context(ExpectedAttemptCount)?
.parse()
.context(ParseAttemptCount)?,
);
lines.next();
for line in lines {
let mut splits = line.splitn(5, '-');
let segment_name = replace(splits.next().context(ExpectedSegmentName)?, r#""?""#, "-");
let mut segment = Segment::new(segment_name);
let split_time =
parse_time(splits.next().context(ExpectedSplitTime)?).context(ParseSplitTime)?;
segment.set_personal_best_split_time(split_time);
let best_segment = parse_time(splits.next().context(ExpectedBestSegmentTime)?)
.context(ParseBestSegmentTime)?;
segment.set_best_segment_time(best_segment);
splits.next();
#[cfg(feature = "std")]
if load_icons {
if let Some(icon_path) = splits.next() {
if !icon_path.is_empty() {
if let Ok(image) = crate::settings::Image::from_file(icon_path, &mut icon_buf) {
segment.set_icon(image);
}
}
}
}
run.push_segment(segment);
}
Ok(run)
}