use crate::{timing, RealTime, Run, Segment, TimeSpan};
use alloc::borrow::Cow;
use core::{num::ParseIntError, result::Result as StdResult};
use snafu::ResultExt;
#[derive(Debug, snafu::Snafu)]
#[snafu(context(suffix(false)))]
pub enum Error {
Empty,
ExpectedCategoryName,
ExpectedAttemptCount,
ParseAttemptCount {
source: ParseIntError,
},
ExpectedSegmentName,
ExpectedSplitTime,
ParseSplitTime {
source: timing::ParseError,
},
ExpectedBestSegment,
ParseBestSegment {
source: timing::ParseError,
},
}
pub type Result<T> = StdResult<T, Error>;
fn unescape(text: &str) -> Cow<'_, str> {
if text.contains('‡') {
text.replace('‡', ",").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();
let line = lines.next().ok_or(Error::Empty)?;
let mut splits = line.split(',');
run.set_category_name(unescape(splits.next().ok_or(Error::ExpectedCategoryName)?));
run.set_attempt_count(
splits
.next()
.ok_or(Error::ExpectedAttemptCount)?
.parse()
.context(ParseAttemptCount)?,
);
for line in &mut lines {
if line.is_empty() {
break;
}
let mut splits = line.split(',');
let mut segment = Segment::new(unescape(splits.next().ok_or(Error::ExpectedSegmentName)?));
let time: TimeSpan = splits
.next()
.ok_or(Error::ExpectedSplitTime)?
.parse()
.context(ParseSplitTime)?;
if time != TimeSpan::zero() {
segment.set_personal_best_split_time(RealTime(Some(time)).into());
}
let time: TimeSpan = splits
.next()
.ok_or(Error::ExpectedBestSegment)?
.parse()
.context(ParseBestSegment)?;
if time != TimeSpan::zero() {
segment.set_best_segment_time(RealTime(Some(time)).into());
}
#[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(
unescape(icon_path).as_ref(),
&mut icon_buf,
) {
segment.set_icon(image);
}
}
}
}
run.push_segment(segment);
}
for line in lines {
if let Some(counter) = line.split(',').next() {
run.metadata_mut()
.custom_variable_mut(unescape(counter))
.permanent()
.set_value("0");
}
}
Ok(run)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn counters() {
const RUN: &str = r#"Run Title:,1
SegmentName,0:00:00.00,0.00
SegmentName,0:00:00.00,0.00
SegmentName,0:00:00.00,0.00
Counter,1,True
Counter2,1,True
"#;
let run = parse(RUN, false).unwrap();
assert_eq!(run.len(), 3);
assert_eq!(run.metadata.custom_variable_value("Counter"), Some("0"));
assert_eq!(run.metadata.custom_variable_value("Counter2"), Some("0"));
}
}