livesplit_core/run/parser/
face_split.rs1use crate::{timing, RealTime, Run, Segment, Time, TimeSpan};
4use alloc::borrow::Cow;
5use core::{num::ParseIntError, result::Result as StdResult};
6use snafu::{OptionExt, ResultExt};
7
8#[derive(Debug, snafu::Snafu)]
11#[snafu(context(suffix(false)))]
12pub enum Error {
13 ExpectedTitle,
15 ExpectedGoal,
17 ExpectedAttemptCount,
19 ParseAttemptCount {
21 source: ParseIntError,
23 },
24 ExpectedSegmentName,
26 ExpectedSplitTime,
28 ParseSplitTime {
30 source: timing::ParseError,
32 },
33 ExpectedBestSegmentTime,
35 ParseBestSegmentTime {
37 source: timing::ParseError,
39 },
40}
41
42pub type Result<T> = StdResult<T, Error>;
44
45fn parse_time(time: &str) -> StdResult<Time, timing::ParseError> {
46 let time: TimeSpan = replace(time, ",", ".").parse()?;
48 if time == TimeSpan::zero() {
50 Ok(Time::default())
51 } else {
52 Ok(RealTime(Some(time)).into())
53 }
54}
55
56fn replace<'text>(text: &'text str, a: &str, b: &str) -> Cow<'text, str> {
57 if text.contains(a) {
58 text.replace(a, b).into()
59 } else {
60 text.into()
61 }
62}
63
64pub fn parse(source: &str, #[allow(unused)] load_icons: bool) -> Result<Run> {
70 let mut run = Run::new();
71 #[cfg(feature = "std")]
72 let mut icon_buf = Vec::new();
73 let mut lines = source.lines();
74
75 run.set_category_name(lines.next().context(ExpectedTitle)?);
76
77 let goal = lines.next().context(ExpectedGoal)?;
78 if !goal.trim_start().is_empty() {
79 run.metadata_mut()
80 .custom_variable_mut("Goal")
81 .permanent()
82 .set_value(goal);
83 }
84
85 run.set_attempt_count(
86 lines
87 .next()
88 .context(ExpectedAttemptCount)?
89 .parse()
90 .context(ParseAttemptCount)?,
91 );
92 lines.next(); for line in lines {
95 let mut splits = line.splitn(5, '-');
96
97 let segment_name = replace(splits.next().context(ExpectedSegmentName)?, r#""?""#, "-");
98 let mut segment = Segment::new(segment_name);
99
100 let split_time =
101 parse_time(splits.next().context(ExpectedSplitTime)?).context(ParseSplitTime)?;
102 segment.set_personal_best_split_time(split_time);
103
104 let best_segment = parse_time(splits.next().context(ExpectedBestSegmentTime)?)
105 .context(ParseBestSegmentTime)?;
106 segment.set_best_segment_time(best_segment);
107
108 splits.next(); #[cfg(feature = "std")]
111 if load_icons {
112 if let Some(icon_path) = splits.next() {
113 if !icon_path.is_empty() {
114 if let Ok(image) = crate::settings::Image::from_file(icon_path, &mut icon_buf) {
115 segment.set_icon(image);
116 }
117 }
118 }
119 }
120
121 run.push_segment(segment);
122 }
123
124 Ok(run)
125}