skip parsed line if expected field not present
This commit is contained in:
@@ -19,8 +19,6 @@ pub enum ParseError {
|
||||
InvalidDecimalNumber,
|
||||
/// Failed to parse game mode.
|
||||
InvalidMode,
|
||||
/// Expected an additional field.
|
||||
MissingField(&'static str),
|
||||
/// Failed to recognized specified type for hitobjects.
|
||||
UnknownHitObjectKind,
|
||||
}
|
||||
@@ -36,7 +34,6 @@ impl fmt::Display for ParseError {
|
||||
Self::InvalidCurvePoints => f.write_str("invalid curve point"),
|
||||
Self::InvalidDecimalNumber => f.write_str("invalid float number"),
|
||||
Self::InvalidMode => f.write_str("invalid mode"),
|
||||
Self::MissingField(field) => write!(f, "missing field `{field}`"),
|
||||
Self::UnknownHitObjectKind => f.write_str("unsupported hitobject kind"),
|
||||
}
|
||||
}
|
||||
@@ -51,7 +48,6 @@ impl StdError for ParseError {
|
||||
Self::InvalidCurvePoints => None,
|
||||
Self::InvalidDecimalNumber => None,
|
||||
Self::InvalidMode => None,
|
||||
Self::MissingField(_) => None,
|
||||
Self::UnknownHitObjectKind => None,
|
||||
}
|
||||
}
|
||||
|
||||
+48
-97
@@ -33,16 +33,6 @@ use crate::{
|
||||
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 {
|
||||
const LIMIT: Self;
|
||||
|
||||
@@ -239,15 +229,8 @@ macro_rules! parse_events_body {
|
||||
|
||||
// We're only interested in breaks
|
||||
if let Some(b'2') = split.next().and_then(|value| value.bytes().next()) {
|
||||
let start_time = split
|
||||
.next()
|
||||
.next_field("break start")
|
||||
.map(f64::parse_in_range)?;
|
||||
|
||||
let end_time = split
|
||||
.next()
|
||||
.next_field("break end")
|
||||
.map(f64::parse_in_range)?;
|
||||
let start_time = split.next().and_then(f64::parse_in_range);
|
||||
let end_time = split.next().and_then(f64::parse_in_range);
|
||||
|
||||
if let (Some(start_time), Some(end_time)) = (start_time, end_time) {
|
||||
$self.breaks.push(Break {
|
||||
@@ -279,13 +262,7 @@ macro_rules! parse_timingpoints_body {
|
||||
let line = $reader.get_line()?;
|
||||
let mut split = line.split(',');
|
||||
|
||||
let time_opt = split
|
||||
.next()
|
||||
.next_field("timing point time")
|
||||
.map(str::trim)
|
||||
.map(f64::parse_in_range)?;
|
||||
|
||||
let time = match time_opt {
|
||||
let time = match split.next().map(str::trim).and_then(f64::parse_in_range) {
|
||||
Some(time) => time,
|
||||
None => continue,
|
||||
};
|
||||
@@ -293,11 +270,10 @@ macro_rules! parse_timingpoints_body {
|
||||
// * beatLength is allowed to be NaN to handle an edge case in which
|
||||
// * some beatmaps use NaN slider velocity to disable slider tick
|
||||
// * generation (see LegacyDifficultyControlPoint).
|
||||
let beat_len: f64 = split.next().next_field("beat len")?.trim().parse()?;
|
||||
|
||||
if !(beat_len.is_in_range() || beat_len.is_nan()) {
|
||||
continue;
|
||||
}
|
||||
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,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let mut timing_change = true;
|
||||
let mut kiai = false;
|
||||
@@ -428,14 +404,12 @@ macro_rules! parse_hitobjects_body {
|
||||
|
||||
let x = split
|
||||
.next()
|
||||
.next_field("x pos")
|
||||
.map(|s| f32::parse_in_custom_range(s, MAX_COORDINATE_VALUE as f32))?
|
||||
.and_then(|s| f32::parse_in_custom_range(s, MAX_COORDINATE_VALUE as f32))
|
||||
.map(|x| x as i32 as f32);
|
||||
|
||||
let y = split
|
||||
.next()
|
||||
.next_field("y pos")
|
||||
.map(|s| f32::parse_in_custom_range(s, MAX_COORDINATE_VALUE as f32))?
|
||||
.and_then(|s| f32::parse_in_custom_range(s, MAX_COORDINATE_VALUE as f32))
|
||||
.map(|x| x as i32 as f32);
|
||||
|
||||
let pos = if let (Some(x), Some(y)) = (x, y) {
|
||||
@@ -444,13 +418,7 @@ macro_rules! parse_hitobjects_body {
|
||||
continue 'next_line;
|
||||
};
|
||||
|
||||
let time_opt = split
|
||||
.next()
|
||||
.next_field("hitobject time")
|
||||
.map(str::trim)
|
||||
.map(f64::parse_in_range)?;
|
||||
|
||||
let time = match time_opt {
|
||||
let time = match split.next().map(str::trim).and_then(f64::parse_in_range) {
|
||||
Some(time) => time,
|
||||
None => continue 'next_line,
|
||||
};
|
||||
@@ -459,64 +427,47 @@ macro_rules! parse_hitobjects_body {
|
||||
unsorted = true;
|
||||
}
|
||||
|
||||
let kind: u8 = match split.next().next_field("hitobject kind")?.parse() {
|
||||
Ok(kind) => kind,
|
||||
Err(_) => continue 'next_line,
|
||||
let kind = match split.next().map(str::parse::<u8>) {
|
||||
Some(Ok(kind)) => kind,
|
||||
_ => continue 'next_line,
|
||||
};
|
||||
|
||||
let mut sound: u8 = match split.next().next_field("sound")?.parse() {
|
||||
Ok(sound) => sound,
|
||||
Err(_) => continue 'next_line,
|
||||
let mut sound = match split.next().map(str::parse::<u8>) {
|
||||
Some(Ok(sound)) => sound,
|
||||
_ => continue 'next_line,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Status {
|
||||
Ok(bool),
|
||||
Skip,
|
||||
Err(ParseError),
|
||||
}
|
||||
|
||||
fn has_custom_sound_file(bank_info: Option<&str>) -> Status {
|
||||
fn has_custom_sound_file(bank_info: Option<&str>) -> Option<bool> {
|
||||
let mut split = match bank_info {
|
||||
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) {
|
||||
Some(Some(_)) => {}
|
||||
Some(None) => return Status::Skip,
|
||||
None => return Status::Err(ParseError::MissingField("normal set")),
|
||||
None => return Some(false),
|
||||
Some(None) => return None,
|
||||
}
|
||||
|
||||
match split.next().map(i32::parse_in_range) {
|
||||
Some(Some(_)) => {}
|
||||
Some(None) => return Status::Skip,
|
||||
None => return Status::Err(ParseError::MissingField("additional set")),
|
||||
}
|
||||
|
||||
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,
|
||||
None => return Some(false),
|
||||
Some(None) => return None,
|
||||
}
|
||||
|
||||
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 {
|
||||
match has_custom_sound_file(split.next()) {
|
||||
Status::Ok(false) => {}
|
||||
Status::Ok(true) => sound = 0,
|
||||
Status::Skip => continue 'next_line,
|
||||
Status::Err(err) => return Err(err),
|
||||
Some(false) => {}
|
||||
Some(true) => sound = 0,
|
||||
None => continue 'next_line,
|
||||
}
|
||||
|
||||
$self.n_circles += 1;
|
||||
@@ -528,13 +479,16 @@ macro_rules! parse_hitobjects_body {
|
||||
// Control Points: [1, 94872] | Median=3 | Mean=2.9984
|
||||
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
|
||||
// * as a repeat, but no repeats are happening
|
||||
Ok(repeats @ 0..=9000) => repeats.saturating_sub(1),
|
||||
Ok(_) | Err(_) => continue 'next_line,
|
||||
Some(Ok(repeats @ 0..=9000)) => repeats.saturating_sub(1),
|
||||
_ => continue 'next_line,
|
||||
};
|
||||
|
||||
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.
|
||||
|
||||
match has_custom_sound_file(split.nth(1)) {
|
||||
Status::Ok(false) => {}
|
||||
Status::Ok(true) => sound = 0,
|
||||
Status::Skip => continue 'next_line,
|
||||
Status::Err(err) => return Err(err),
|
||||
Some(false) => {}
|
||||
Some(true) => sound = 0,
|
||||
None => continue 'next_line,
|
||||
}
|
||||
|
||||
HitObjectKind::Slider {
|
||||
@@ -638,16 +591,15 @@ macro_rules! parse_hitobjects_body {
|
||||
} else if kind & Self::SPINNER_FLAG > 0 {
|
||||
$self.n_spinners += 1;
|
||||
|
||||
let end_time = match split.next().next_field("spinner endtime")?.parse::<f64>() {
|
||||
Ok(end_time) => end_time.max(time),
|
||||
Err(_) => continue 'next_line,
|
||||
let end_time = match split.next().map(str::parse::<f64>) {
|
||||
Some(Ok(end_time)) => end_time.max(time),
|
||||
_ => continue 'next_line,
|
||||
};
|
||||
|
||||
match has_custom_sound_file(split.next()) {
|
||||
Status::Ok(false) => {}
|
||||
Status::Ok(true) => sound = 0,
|
||||
Status::Skip => continue 'next_line,
|
||||
Status::Err(err) => return Err(err),
|
||||
Some(false) => {}
|
||||
Some(true) => sound = 0,
|
||||
None => continue 'next_line,
|
||||
}
|
||||
|
||||
HitObjectKind::Spinner { end_time }
|
||||
@@ -662,10 +614,9 @@ macro_rules! parse_hitobjects_body {
|
||||
};
|
||||
|
||||
match has_custom_sound_file(Some(tail)) {
|
||||
Status::Ok(false) => {}
|
||||
Status::Ok(true) => sound = 0,
|
||||
Status::Skip => continue 'next_line,
|
||||
Status::Err(err) => return Err(err),
|
||||
Some(false) => {}
|
||||
Some(true) => sound = 0,
|
||||
None => continue 'next_line,
|
||||
}
|
||||
|
||||
parsed
|
||||
|
||||
Reference in New Issue
Block a user