skip parsed line if expected field not present

This commit is contained in:
MaxOhn
2023-02-09 07:08:35 +01:00
parent 6c36065ea1
commit d15f3313d9
2 changed files with 48 additions and 101 deletions
-4
View File
@@ -19,8 +19,6 @@ pub enum ParseError {
InvalidDecimalNumber, InvalidDecimalNumber,
/// Failed to parse game mode. /// Failed to parse game mode.
InvalidMode, InvalidMode,
/// Expected an additional field.
MissingField(&'static str),
/// Failed to recognized specified type for hitobjects. /// Failed to recognized specified type for hitobjects.
UnknownHitObjectKind, UnknownHitObjectKind,
} }
@@ -36,7 +34,6 @@ impl fmt::Display for ParseError {
Self::InvalidCurvePoints => f.write_str("invalid curve point"), Self::InvalidCurvePoints => f.write_str("invalid curve point"),
Self::InvalidDecimalNumber => f.write_str("invalid float number"), Self::InvalidDecimalNumber => f.write_str("invalid float number"),
Self::InvalidMode => f.write_str("invalid mode"), Self::InvalidMode => f.write_str("invalid mode"),
Self::MissingField(field) => write!(f, "missing field `{field}`"),
Self::UnknownHitObjectKind => f.write_str("unsupported hitobject kind"), Self::UnknownHitObjectKind => f.write_str("unsupported hitobject kind"),
} }
} }
@@ -51,7 +48,6 @@ impl StdError for ParseError {
Self::InvalidCurvePoints => None, Self::InvalidCurvePoints => None,
Self::InvalidDecimalNumber => None, Self::InvalidDecimalNumber => None,
Self::InvalidMode => None, Self::InvalidMode => None,
Self::MissingField(_) => None,
Self::UnknownHitObjectKind => None, Self::UnknownHitObjectKind => None,
} }
} }
+48 -97
View File
@@ -33,16 +33,6 @@ use crate::{
util::{SortedVec, TandemSorter}, util::{SortedVec, TandemSorter},
}; };
trait OptionExt<T> {
fn next_field(self, field: &'static str) -> Result<T, ParseError>;
}
impl<T> OptionExt<T> for Option<T> {
fn next_field(self, field: &'static str) -> Result<T, ParseError> {
self.ok_or(ParseError::MissingField(field))
}
}
trait InRange: Sized + Copy + Neg<Output = Self> + PartialOrd + FromStr { trait InRange: Sized + Copy + Neg<Output = Self> + PartialOrd + FromStr {
const LIMIT: Self; const LIMIT: Self;
@@ -239,15 +229,8 @@ macro_rules! parse_events_body {
// We're only interested in breaks // We're only interested in breaks
if let Some(b'2') = split.next().and_then(|value| value.bytes().next()) { if let Some(b'2') = split.next().and_then(|value| value.bytes().next()) {
let start_time = split let start_time = split.next().and_then(f64::parse_in_range);
.next() let end_time = split.next().and_then(f64::parse_in_range);
.next_field("break start")
.map(f64::parse_in_range)?;
let end_time = split
.next()
.next_field("break end")
.map(f64::parse_in_range)?;
if let (Some(start_time), Some(end_time)) = (start_time, end_time) { if let (Some(start_time), Some(end_time)) = (start_time, end_time) {
$self.breaks.push(Break { $self.breaks.push(Break {
@@ -279,13 +262,7 @@ macro_rules! parse_timingpoints_body {
let line = $reader.get_line()?; let line = $reader.get_line()?;
let mut split = line.split(','); let mut split = line.split(',');
let time_opt = split let time = match split.next().map(str::trim).and_then(f64::parse_in_range) {
.next()
.next_field("timing point time")
.map(str::trim)
.map(f64::parse_in_range)?;
let time = match time_opt {
Some(time) => time, Some(time) => time,
None => continue, None => continue,
}; };
@@ -293,11 +270,10 @@ macro_rules! parse_timingpoints_body {
// * beatLength is allowed to be NaN to handle an edge case in which // * beatLength is allowed to be NaN to handle an edge case in which
// * some beatmaps use NaN slider velocity to disable slider tick // * some beatmaps use NaN slider velocity to disable slider tick
// * generation (see LegacyDifficultyControlPoint). // * generation (see LegacyDifficultyControlPoint).
let beat_len: f64 = split.next().next_field("beat len")?.trim().parse()?; let beat_len = match split.next().map(str::trim).map(str::parse::<f64>) {
Some(Ok(beat_len)) if beat_len.is_in_range() || beat_len.is_nan() => beat_len,
if !(beat_len.is_in_range() || beat_len.is_nan()) { _ => continue,
continue; };
}
let mut timing_change = true; let mut timing_change = true;
let mut kiai = false; let mut kiai = false;
@@ -428,14 +404,12 @@ macro_rules! parse_hitobjects_body {
let x = split let x = split
.next() .next()
.next_field("x pos") .and_then(|s| f32::parse_in_custom_range(s, MAX_COORDINATE_VALUE as f32))
.map(|s| f32::parse_in_custom_range(s, MAX_COORDINATE_VALUE as f32))?
.map(|x| x as i32 as f32); .map(|x| x as i32 as f32);
let y = split let y = split
.next() .next()
.next_field("y pos") .and_then(|s| f32::parse_in_custom_range(s, MAX_COORDINATE_VALUE as f32))
.map(|s| f32::parse_in_custom_range(s, MAX_COORDINATE_VALUE as f32))?
.map(|x| x as i32 as f32); .map(|x| x as i32 as f32);
let pos = if let (Some(x), Some(y)) = (x, y) { let pos = if let (Some(x), Some(y)) = (x, y) {
@@ -444,13 +418,7 @@ macro_rules! parse_hitobjects_body {
continue 'next_line; continue 'next_line;
}; };
let time_opt = split let time = match split.next().map(str::trim).and_then(f64::parse_in_range) {
.next()
.next_field("hitobject time")
.map(str::trim)
.map(f64::parse_in_range)?;
let time = match time_opt {
Some(time) => time, Some(time) => time,
None => continue 'next_line, None => continue 'next_line,
}; };
@@ -459,64 +427,47 @@ macro_rules! parse_hitobjects_body {
unsorted = true; unsorted = true;
} }
let kind: u8 = match split.next().next_field("hitobject kind")?.parse() { let kind = match split.next().map(str::parse::<u8>) {
Ok(kind) => kind, Some(Ok(kind)) => kind,
Err(_) => continue 'next_line, _ => continue 'next_line,
}; };
let mut sound: u8 = match split.next().next_field("sound")?.parse() { let mut sound = match split.next().map(str::parse::<u8>) {
Ok(sound) => sound, Some(Ok(sound)) => sound,
Err(_) => continue 'next_line, _ => continue 'next_line,
}; };
#[derive(Debug)] fn has_custom_sound_file(bank_info: Option<&str>) -> Option<bool> {
enum Status {
Ok(bool),
Skip,
Err(ParseError),
}
fn has_custom_sound_file(bank_info: Option<&str>) -> Status {
let mut split = match bank_info { let mut split = match bank_info {
Some(s) if !s.is_empty() => s.split(':'), Some(s) if !s.is_empty() => s.split(':'),
_ => return Status::Ok(false), _ => return Some(false),
}; };
split.next().and_then(i32::parse_in_range)?;
split.next().and_then(i32::parse_in_range)?;
match split.next().map(i32::parse_in_range) { match split.next().map(i32::parse_in_range) {
Some(Some(_)) => {} Some(Some(_)) => {}
Some(None) => return Status::Skip, None => return Some(false),
None => return Status::Err(ParseError::MissingField("normal set")), Some(None) => return None,
} }
match split.next().map(i32::parse_in_range) { match split.next().map(i32::parse_in_range) {
Some(Some(_)) => {} Some(Some(_)) => {}
Some(None) => return Status::Skip, None => return Some(false),
None => return Status::Err(ParseError::MissingField("additional set")), Some(None) => return None,
}
match split.next().map(i32::parse_in_range) {
Some(Some(_)) => {}
None => return Status::Ok(false),
Some(None) => return Status::Skip,
}
match split.next().map(i32::parse_in_range) {
Some(Some(_)) => {}
None => return Status::Ok(false),
Some(None) => return Status::Skip,
} }
let filename = split.next().filter(|filename| !filename.is_empty()); let filename = split.next().filter(|filename| !filename.is_empty());
Status::Ok(filename.is_some()) Some(filename.is_some())
} }
let kind = if kind & Self::CIRCLE_FLAG > 0 { let kind = if kind & Self::CIRCLE_FLAG > 0 {
match has_custom_sound_file(split.next()) { match has_custom_sound_file(split.next()) {
Status::Ok(false) => {} Some(false) => {}
Status::Ok(true) => sound = 0, Some(true) => sound = 0,
Status::Skip => continue 'next_line, None => continue 'next_line,
Status::Err(err) => return Err(err),
} }
$self.n_circles += 1; $self.n_circles += 1;
@@ -528,13 +479,16 @@ macro_rules! parse_hitobjects_body {
// Control Points: [1, 94872] | Median=3 | Mean=2.9984 // Control Points: [1, 94872] | Median=3 | Mean=2.9984
let mut control_points = Vec::with_capacity(3); let mut control_points = Vec::with_capacity(3);
let control_point_iter = split.next().next_field("control points")?.split('|'); let control_point_iter = match split.next() {
Some(s) => s.split('|'),
None => continue 'next_line,
};
let repeats = match split.next().next_field("repeats")?.parse::<usize>() { let repeats = match split.next().map(str::parse::<usize>) {
// * osu-stable treated the first span of the slider // * osu-stable treated the first span of the slider
// * as a repeat, but no repeats are happening // * as a repeat, but no repeats are happening
Ok(repeats @ 0..=9000) => repeats.saturating_sub(1), Some(Ok(repeats @ 0..=9000)) => repeats.saturating_sub(1),
Ok(_) | Err(_) => continue 'next_line, _ => continue 'next_line,
}; };
let mut start_idx = 0; let mut start_idx = 0;
@@ -622,10 +576,9 @@ macro_rules! parse_hitobjects_body {
// Note: Edge sets are currently not considered, seems to be fine though. // Note: Edge sets are currently not considered, seems to be fine though.
match has_custom_sound_file(split.nth(1)) { match has_custom_sound_file(split.nth(1)) {
Status::Ok(false) => {} Some(false) => {}
Status::Ok(true) => sound = 0, Some(true) => sound = 0,
Status::Skip => continue 'next_line, None => continue 'next_line,
Status::Err(err) => return Err(err),
} }
HitObjectKind::Slider { HitObjectKind::Slider {
@@ -638,16 +591,15 @@ macro_rules! parse_hitobjects_body {
} else if kind & Self::SPINNER_FLAG > 0 { } else if kind & Self::SPINNER_FLAG > 0 {
$self.n_spinners += 1; $self.n_spinners += 1;
let end_time = match split.next().next_field("spinner endtime")?.parse::<f64>() { let end_time = match split.next().map(str::parse::<f64>) {
Ok(end_time) => end_time.max(time), Some(Ok(end_time)) => end_time.max(time),
Err(_) => continue 'next_line, _ => continue 'next_line,
}; };
match has_custom_sound_file(split.next()) { match has_custom_sound_file(split.next()) {
Status::Ok(false) => {} Some(false) => {}
Status::Ok(true) => sound = 0, Some(true) => sound = 0,
Status::Skip => continue 'next_line, None => continue 'next_line,
Status::Err(err) => return Err(err),
} }
HitObjectKind::Spinner { end_time } HitObjectKind::Spinner { end_time }
@@ -662,10 +614,9 @@ macro_rules! parse_hitobjects_body {
}; };
match has_custom_sound_file(Some(tail)) { match has_custom_sound_file(Some(tail)) {
Status::Ok(false) => {} Some(false) => {}
Status::Ok(true) => sound = 0, Some(true) => sound = 0,
Status::Skip => continue 'next_line, None => continue 'next_line,
Status::Err(err) => return Err(err),
} }
parsed parsed