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§

source

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.

source

type State: DecodeState + Into<Self>

The parsing state which will be updated on each line and turned into Self at the end.

Required Methods§

source

fn parse_general(state: &mut Self::State, line: &str) -> Result<(), Self::Error>

Update the state based on a line of the [General] section.

source

fn parse_editor(state: &mut Self::State, line: &str) -> Result<(), Self::Error>

Update the state based on a line of the [Editor] section.

source

fn parse_metadata( state: &mut Self::State, line: &str ) -> Result<(), Self::Error>

Update the state based on a line of the [Metadata] section.

source

fn parse_difficulty( state: &mut Self::State, line: &str ) -> Result<(), Self::Error>

Update the state based on a line of the [Difficulty] section.

source

fn parse_events(state: &mut Self::State, line: &str) -> Result<(), Self::Error>

Update the state based on a line of the [Events] section.

source

fn parse_timing_points( state: &mut Self::State, line: &str ) -> Result<(), Self::Error>

Update the state based on a line of the [TimingPoints] section.

source

fn parse_colors(state: &mut Self::State, line: &str) -> Result<(), Self::Error>

Update the state based on a line of the [Colours] section.

source

fn parse_hit_objects( state: &mut Self::State, line: &str ) -> Result<(), Self::Error>

Update the state based on a line of the [HitObjects] section.

source

fn parse_variables( state: &mut Self::State, line: &str ) -> Result<(), Self::Error>

Update the state based on a line of the [Variables] section.

source

fn parse_catch_the_beat( state: &mut Self::State, line: &str ) -> Result<(), Self::Error>

Update the state based on a line of the [CatchTheBeat] section.

source

fn parse_mania(state: &mut Self::State, line: &str) -> Result<(), Self::Error>

Update the state based on a line of the [Mania] section.

Provided Methods§

source

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.

source

fn should_skip_line(line: &str) -> bool

Whether a line should not be forwarded to the parsing methods.

Object Safety§

This trait is not object safe.

Implementors§