Trait rosu_map::DecodeBeatmap
source · pub trait DecodeBeatmap: Sized {
type Error: Error;
type State: DecodeState + Into<Self>;
Show 13 methods
// Required methods
fn parse_general(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>;
fn parse_editor(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>;
fn parse_metadata(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>;
fn parse_difficulty(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>;
fn parse_events(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>;
fn parse_timing_points(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>;
fn parse_colors(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>;
fn parse_hit_objects(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>;
fn parse_variables(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>;
fn parse_catch_the_beat(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>;
fn parse_mania(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>;
// Provided methods
fn decode<R: BufRead>(src: R) -> Result<Self, Error> { ... }
fn should_skip_line(line: &str) -> bool { ... }
}
Expand description
Trait to handle reading and parsing content of .osu
files.
Generally, the only way to interact with this trait should be calling the
decode
method.
Each section has its own parse_[section]
method in which, given the next
line, the state should be updated. Note that the given lines will be
non-empty but comments (text starting with //
) are not trimmed.
§Example
DecodeBeatmap
is implemented for structs like HitObjects
or
Beatmap
so it can be used out the box.
use std::io::Cursor;
use rosu_map::{Beatmap, DecodeBeatmap};
use rosu_map::section::general::GameMode;
use rosu_map::section::hit_objects::HitObjects;
let content: &str = "osu file format v14
[General]
Mode: 1 // Some comment
[Metadata]
Title: Some song title";
// Converting &str to &[u8] so that io::BufRead is satisfied
let mut reader = content.as_bytes();
let decoded = HitObjects::decode(&mut reader).unwrap();
assert_eq!(decoded.mode, GameMode::Taiko);
assert!(decoded.hit_objects.is_empty());
let mut reader = content.as_bytes();
let decoded = Beatmap::decode(&mut reader).unwrap();
assert_eq!(decoded.mode, GameMode::Taiko);
assert_eq!(decoded.title, "Some song title");
Let’s assume only the beatmap title and difficulty attributes are of
interest. Using Beatmap
will parse everything which will be much
slower than implementing this trait on a custom type:
use rosu_map::{DecodeBeatmap, DecodeState};
use rosu_map::section::difficulty::{Difficulty, DifficultyState, ParseDifficultyError};
use rosu_map::section::metadata::MetadataKey;
use rosu_map::util::KeyValue;
// Our final struct that we want to parse into.
struct CustomBeatmap {
title: String,
ar: f32,
cs: f32,
hp: f32,
od: f32,
}
// The struct that will be built gradually while parsing.
struct CustomBeatmapState {
title: String,
// Built-in way to handle difficulty parsing.
difficulty: DifficultyState,
}
// Required to implement for the `DecodeBeatmap` trait.
impl DecodeState for CustomBeatmapState {
fn create(version: i32) -> Self {
Self {
title: String::new(),
difficulty: DifficultyState::create(version),
}
}
}
// Also required for the `DecodeBeatmap` trait
impl From<CustomBeatmapState> for CustomBeatmap {
fn from(state: CustomBeatmapState) -> Self {
let difficulty = Difficulty::from(state.difficulty);
Self {
title: state.title,
ar: difficulty.approach_rate,
cs: difficulty.circle_size,
hp: difficulty.hp_drain_rate,
od: difficulty.overall_difficulty,
}
}
}
impl DecodeBeatmap for CustomBeatmap {
type State = CustomBeatmapState;
// In our case, only parsing the difficulty can fail so we can just use
// its error type.
type Error = ParseDifficultyError;
fn parse_metadata(state: &mut Self::State, line: &str) -> Result<(), Self::Error> {
// Note that comments are *not* trimmed at this point.
// To do that, one can use the `rosu_map::util::StrExt` trait and
// its `trim_comment` method.
let Ok(KeyValue { key, value }) = KeyValue::parse(line) else {
// Unknown key, discard line
return Ok(());
};
match key {
MetadataKey::Title => state.title = value.to_owned(),
_ => {}
}
Ok(())
}
fn parse_difficulty(state: &mut Self::State, line: &str) -> Result<(), Self::Error> {
// Let `Difficulty` and its state handle the difficulty parsing.
Difficulty::parse_difficulty(&mut state.difficulty, line)
}
// None of the other sections are of interest.
fn parse_general(_state: &mut Self::State, _line: &str) -> Result<(), Self::Error> {
Ok(())
}
fn parse_editor(_state: &mut Self::State, _line: &str) -> Result<(), Self::Error> {
Ok(())
}
fn parse_events(_state: &mut Self::State, _line: &str) -> Result<(), Self::Error> {
Ok(())
}
fn parse_timing_points(_state: &mut Self::State, _line: &str) -> Result<(), Self::Error> {
Ok(())
}
fn parse_colors(_state: &mut Self::State, _line: &str) -> Result<(), Self::Error> {
Ok(())
}
fn parse_hit_objects(_state: &mut Self::State, _line: &str) -> Result<(), Self::Error> {
Ok(())
}
fn parse_variables(_state: &mut Self::State, _line: &str) -> Result<(), Self::Error> {
Ok(())
}
fn parse_catch_the_beat(_state: &mut Self::State, _line: &str) -> Result<(), Self::Error> {
Ok(())
}
fn parse_mania(_state: &mut Self::State, _line: &str) -> Result<(), Self::Error> {
Ok(())
}
}
For more examples, check out how structs like TimingPoints
or
Beatmap
implement the DecodeBeatmap
trait.
Required Associated Types§
sourcetype Error: Error
type Error: Error
Error type in case something goes wrong while parsing.
Note that this error is not thrown by the decode
method. Instead,
when a parse_[section]
method returns such an error, it will be
handled silently. That means, if the tracing
feature is enabled, the
error and its causes will be logged on the ERROR
level. If tracing
is not enabled, the error will be ignored entirely.
sourcetype State: DecodeState + Into<Self>
type State: DecodeState + Into<Self>
The parsing state which will be updated on each line and turned into
Self
at the end.
Required Methods§
sourcefn parse_general(state: &mut Self::State, line: &str) -> Result<(), Self::Error>
fn parse_general(state: &mut Self::State, line: &str) -> Result<(), Self::Error>
Update the state based on a line of the [General]
section.
sourcefn parse_editor(state: &mut Self::State, line: &str) -> Result<(), Self::Error>
fn parse_editor(state: &mut Self::State, line: &str) -> Result<(), Self::Error>
Update the state based on a line of the [Editor]
section.
sourcefn parse_metadata(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>
fn parse_metadata( state: &mut Self::State, line: &str ) -> Result<(), Self::Error>
Update the state based on a line of the [Metadata]
section.
sourcefn parse_difficulty(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>
fn parse_difficulty( state: &mut Self::State, line: &str ) -> Result<(), Self::Error>
Update the state based on a line of the [Difficulty]
section.
sourcefn parse_events(state: &mut Self::State, line: &str) -> Result<(), Self::Error>
fn parse_events(state: &mut Self::State, line: &str) -> Result<(), Self::Error>
Update the state based on a line of the [Events]
section.
sourcefn parse_timing_points(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>
fn parse_timing_points( state: &mut Self::State, line: &str ) -> Result<(), Self::Error>
Update the state based on a line of the [TimingPoints]
section.
sourcefn parse_colors(state: &mut Self::State, line: &str) -> Result<(), Self::Error>
fn parse_colors(state: &mut Self::State, line: &str) -> Result<(), Self::Error>
Update the state based on a line of the [Colours]
section.
sourcefn parse_hit_objects(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>
fn parse_hit_objects( state: &mut Self::State, line: &str ) -> Result<(), Self::Error>
Update the state based on a line of the [HitObjects]
section.
sourcefn parse_variables(
state: &mut Self::State,
line: &str
) -> Result<(), Self::Error>
fn parse_variables( state: &mut Self::State, line: &str ) -> Result<(), Self::Error>
Update the state based on a line of the [Variables]
section.
Provided Methods§
sourcefn decode<R: BufRead>(src: R) -> Result<Self, Error>
fn decode<R: BufRead>(src: R) -> Result<Self, Error>
The key method to read and parse content of a .osu
file into Self
.
This method should not be implemented manually.
sourcefn should_skip_line(line: &str) -> bool
fn should_skip_line(line: &str) -> bool
Whether a line should not be forwarded to the parsing methods.