Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Borrow as much as possible in the parsers #546

Merged
merged 1 commit into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion capi/src/parse_run_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use livesplit_core::run::parser::{composite::ParsedRun, TimerKind};
use std::{io::Write, os::raw::c_char};

/// type
pub type ParseRunResult = Option<ParsedRun>;
pub type ParseRunResult = Option<ParsedRun<'static>>;
/// type
pub type OwnedParseRunResult = Box<ParseRunResult>;

Expand Down
3 changes: 2 additions & 1 deletion capi/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ pub unsafe extern "C" fn Run_parse_file_handle(
Box::new(
file.read_to_end(buf)
.ok()
.and_then(|_| parser::composite::parse(buf, path, load_files).ok()),
.and_then(|_| parser::composite::parse(buf, path, load_files).ok())
.map(|p| p.into_owned()),
)
})
}
Expand Down
6 changes: 3 additions & 3 deletions src/layout/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ mod timer;
mod title;
mod total_playtime;

#[cfg(windows)]
#[cfg(all(windows, feature = "std"))]
mod font_resolving;

// One single row component is:
Expand Down Expand Up @@ -394,7 +394,7 @@ where
// receive the byte representation of individual tables we query for, so
// we can get the family name from the `name` table.

#[cfg(windows)]
#[cfg(all(windows, feature = "std"))]
let family = if let Some(info) =
font_resolving::FontInfo::from_gdi(original_family_name, bold_flag, italic_flag)
{
Expand All @@ -421,7 +421,7 @@ where
family.to_owned()
};

#[cfg(not(windows))]
#[cfg(not(all(windows, feature = "std")))]
let family = family.to_owned();

// The font might not exist on the user's system, so we still prefer to
Expand Down
5 changes: 3 additions & 2 deletions src/networking/splits_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ pub enum DownloadError {
pub async fn download_run(
client: &Client,
id: &str,
) -> Result<composite::ParsedRun, DownloadError> {
) -> Result<composite::ParsedRun<'static>, DownloadError> {
let bytes = api::run::download(client, id).await.context(Download)?;
composite::parse(&bytes, None, false).context(Parse)
let run = composite::parse(&bytes, None, false).context(Parse)?;
Ok(run.into_owned())
}

/// Asynchronously uploads a run to Splits.io. An object representing the ID of
Expand Down
20 changes: 15 additions & 5 deletions src/run/parser/composite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,24 @@ pub type Result<T> = StdResult<T, Error>;

/// A run parsed by the Composite Parser. This contains the Run itself and
/// information about which parser parsed it.
pub struct ParsedRun {
pub struct ParsedRun<'a> {
/// The parsed run.
pub run: Run,
/// The parser that parsed it.
pub kind: TimerKind,
pub kind: TimerKind<'a>,
}

const fn parsed(run: Run, kind: TimerKind) -> ParsedRun {
impl ParsedRun<'_> {
/// Returns an owned version of the parsed run.
pub fn into_owned(self) -> ParsedRun<'static> {
ParsedRun {
run: self.run,
kind: self.kind.into_owned(),
}
}
}

const fn parsed(run: Run, kind: TimerKind<'_>) -> ParsedRun<'_> {
ParsedRun { run, kind }
}

Expand All @@ -74,7 +84,7 @@ pub fn parse_and_fix<R>(
source: &[u8],
path: Option<PathBuf>,
load_files: bool,
) -> Result<ParsedRun> {
) -> Result<ParsedRun<'_>> {
let mut run = parse(source, path, load_files)?;
run.run.fix_splits();
Ok(run)
Expand All @@ -86,7 +96,7 @@ pub fn parse_and_fix<R>(
/// additional files, like external images are allowed to be loaded. If you are
/// using livesplit-core in a server-like environment, set this to `false`. Only
/// client-side applications should set this to `true`.
pub fn parse(source: &[u8], path: Option<PathBuf>, load_files: bool) -> Result<ParsedRun> {
pub fn parse(source: &[u8], path: Option<PathBuf>, load_files: bool) -> Result<ParsedRun<'_>> {
if let Ok(source) = simdutf8::basic::from_utf8(source) {
let files_path = if load_files { path.clone() } else { None };

Expand Down
11 changes: 6 additions & 5 deletions src/run/parser/flitter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ pub use self::s_expressions::Error;
pub type Result<T> = StdResult<T, Error>;

#[derive(Deserialize)]
struct Splits {
title: String,
category: String,
struct Splits<'a> {
title: &'a str,
category: &'a str,
attempts: u32,
split_names: Vec<String>,
#[serde(borrow)]
split_names: Vec<&'a str>,
golds: Option<Vec<Gold>>,
personal_best: Option<Comparison>,
world_record: Option<Comparison>,
Expand All @@ -39,7 +40,7 @@ struct Split {

/// Attempts to parse a Flitter splits file.
pub fn parse(source: &str) -> Result<Run> {
let splits: Splits = self::s_expressions::from_str(source)?;
let splits: Splits<'_> = self::s_expressions::from_str(source)?;

let mut run = Run::new();

Expand Down
25 changes: 14 additions & 11 deletions src/run/parser/flitter/s_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

use crate::platform::prelude::*;
use core::{fmt::Display, num::ParseIntError};
use serde::de::{self, DeserializeOwned, DeserializeSeed, MapAccess, SeqAccess, Visitor};
use serde::{
de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor},
Deserialize,
};

/// The Error types for splits files that couldn't be parsed by the Flitter
/// Parser.
Expand Down Expand Up @@ -80,7 +83,7 @@ impl<'source> Deserializer<'source> {
}
}

fn parse_ident(&mut self) -> &str {
fn parse_ident(&mut self) -> &'source str {
if let Some((pos, _)) = self
.source
.char_indices()
Expand All @@ -94,7 +97,7 @@ impl<'source> Deserializer<'source> {
}
}

fn parse_string(&mut self) -> Result<&str> {
fn parse_string(&mut self) -> Result<&'source str> {
if let Some(rem) = self.source.strip_prefix('"') {
if let Some((in_str, after)) = rem.split_once('"') {
self.source = after;
Expand Down Expand Up @@ -137,9 +140,9 @@ impl<'source> Deserializer<'source> {
}
}

pub fn from_str<T>(source: &str) -> Result<T>
pub fn from_str<'de, T>(source: &'de str) -> Result<T>
where
T: DeserializeOwned,
T: 'de + Deserialize<'de>,
{
let mut deserializer = Deserializer::from_str(source);
let t = T::deserialize(&mut deserializer)?;
Expand All @@ -151,7 +154,7 @@ where
}
}

impl<'de, 'source> de::Deserializer<'de> for &mut Deserializer<'source> {
impl<'de> de::Deserializer<'de> for &mut Deserializer<'de> {
type Error = Error;

fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
Expand Down Expand Up @@ -236,13 +239,13 @@ impl<'de, 'source> de::Deserializer<'de> for &mut Deserializer<'source> {
where
V: Visitor<'de>,
{
visitor.visit_str(self.parse_string()?)
visitor.visit_borrowed_str(self.parse_string()?)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_str(self.parse_string()?)
visitor.visit_borrowed_str(self.parse_string()?)
}
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
where
Expand Down Expand Up @@ -355,7 +358,7 @@ impl<'de, 'source> de::Deserializer<'de> for &mut Deserializer<'source> {
where
V: Visitor<'de>,
{
visitor.visit_str(self.parse_ident())
visitor.visit_borrowed_str(self.parse_ident())
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
where
Expand All @@ -366,7 +369,7 @@ impl<'de, 'source> de::Deserializer<'de> for &mut Deserializer<'source> {
}
}

impl<'de, 'source> SeqAccess<'de> for &mut Deserializer<'source> {
impl<'de> SeqAccess<'de> for &mut Deserializer<'de> {
type Error = Error;

fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
Expand All @@ -382,7 +385,7 @@ impl<'de, 'source> SeqAccess<'de> for &mut Deserializer<'source> {
}
}

impl<'de, 'source> MapAccess<'de> for &mut Deserializer<'source> {
impl<'de> MapAccess<'de> for &mut Deserializer<'de> {
type Error = Error;

fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
Expand Down
3 changes: 3 additions & 0 deletions src/run/parser/llanfair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ pub fn parse(source: &[u8]) -> Result<Run> {

// Seek to the first byte of the first segment
cursor = cursor.get(0x8F..).context(ReadSegment)?;

run.segments_mut().reserve(segment_count as usize);

for _ in 0..segment_count {
#[cfg(feature = "std")]
let mut icon = None;
Expand Down
44 changes: 23 additions & 21 deletions src/run/parser/source_live_timer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Provides the parser for the SourceLiveTimer splits files.

use crate::{platform::prelude::*, GameTime, Run, Segment, TimeSpan};
use alloc::borrow::Cow;
use core::result::Result as StdResult;
use serde::Deserialize;
use serde_json::Error as JsonError;
Expand All @@ -23,23 +24,27 @@ pub type Result<T> = StdResult<T, Error>;

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct Splits {
Category: String,
RunName: Option<String>,
Splits: Option<Vec<Split>>,
struct Splits<'a> {
#[serde(borrow)]
Category: Cow<'a, str>,
#[serde(borrow)]
RunName: Option<Cow<'a, str>>,
Splits: Option<Vec<Split<'a>>>,
}

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct Split {
Map: String,
Name: Option<String>,
struct Split<'a> {
#[serde(borrow)]
Map: Cow<'a, str>,
#[serde(borrow)]
Name: Option<Cow<'a, str>>,
Ticks: Option<u64>,
BestSegment: Option<u64>,
}

fn time_span_from_ticks(category_name: &str, ticks: u64) -> TimeSpan {
let seconds = if category_name.starts_with("Portal 2") {
fn time_span_from_ticks(is_portal2: bool, ticks: u64) -> TimeSpan {
let seconds = if is_portal2 {
ticks as f64 / 30.0
} else {
// Game is either Portal or Half Life 2
Expand All @@ -51,7 +56,8 @@ fn time_span_from_ticks(category_name: &str, ticks: u64) -> TimeSpan {

/// Attempts to parse a SourceLiveTimer splits file.
pub fn parse(source: &str) -> Result<Run> {
let splits: Splits = serde_json::from_str(source).map_err(|source| Error::Json { source })?;
let splits: Splits<'_> =
serde_json::from_str(source).map_err(|source| Error::Json { source })?;

let mut run = Run::new();

Expand All @@ -74,27 +80,23 @@ pub fn parse(source: &str) -> Result<Run> {
}

if let Some(segments) = splits.Splits {
for split in segments {
let name = if let Some(name) = split.Name {
name.to_owned()
} else {
split.Map.to_owned()
};
let is_portal2 = run.category_name().starts_with("Portal 2");

let mut segment = Segment::new(name);
run.segments_mut().extend(segments.into_iter().map(|split| {
let mut segment = Segment::new(split.Name.unwrap_or(split.Map));

if let Some(ticks) = split.Ticks {
let pb_split_time = Some(time_span_from_ticks(run.category_name(), ticks));
let pb_split_time = Some(time_span_from_ticks(is_portal2, ticks));
segment.set_personal_best_split_time(GameTime(pb_split_time).into());
}

if let Some(best) = split.BestSegment {
let best_segment_time = Some(time_span_from_ticks(run.category_name(), best));
let best_segment_time = Some(time_span_from_ticks(is_portal2, best));
segment.set_best_segment_time(GameTime(best_segment_time).into());
}

run.push_segment(segment);
}
segment
}));
}

Ok(run)
Expand Down
Loading