rosu_map/section/
editor.rs

1use crate::{
2    decode::{DecodeBeatmap, DecodeState},
3    util::{KeyValue, ParseNumberError, StrExt},
4    Beatmap,
5};
6
7/// Struct containing all data from a `.osu` file's `[Editor]` section.
8#[derive(Clone, Debug, PartialEq)]
9pub struct Editor {
10    pub bookmarks: Vec<i32>,
11    pub distance_spacing: f64,
12    pub beat_divisor: i32,
13    pub grid_size: i32,
14    pub timeline_zoom: f64,
15}
16
17impl Default for Editor {
18    #[allow(clippy::default_trait_access)]
19    fn default() -> Self {
20        Self {
21            bookmarks: Default::default(),
22            distance_spacing: 1.0,
23            beat_divisor: 4,
24            grid_size: Default::default(),
25            timeline_zoom: 1.0,
26        }
27    }
28}
29
30impl From<Editor> for Beatmap {
31    fn from(editor: Editor) -> Self {
32        Self {
33            bookmarks: editor.bookmarks,
34            distance_spacing: editor.distance_spacing,
35            beat_divisor: editor.beat_divisor,
36            grid_size: editor.grid_size,
37            timeline_zoom: editor.timeline_zoom,
38            ..Self::default()
39        }
40    }
41}
42
43section_keys! {
44    /// All valid keys within a `.osu` file's `[Editor]` section
45    pub enum EditorKey {
46        Bookmarks,
47        DistanceSpacing,
48        BeatDivisor,
49        GridSize,
50        TimelineZoom,
51    }
52}
53
54thiserror! {
55    /// All the ways that parsing a `.osu` file into [`Editor`] can fail.
56    #[derive(Debug)]
57    pub enum ParseEditorError {
58        #[error("failed to parse number")]
59        Number(#[from] ParseNumberError),
60    }
61}
62
63/// The parsing state for [`Editor`] in [`DecodeBeatmap`].
64pub type EditorState = Editor;
65
66impl DecodeState for EditorState {
67    fn create(_: i32) -> Self {
68        Self::default()
69    }
70}
71
72impl DecodeBeatmap for Editor {
73    type Error = ParseEditorError;
74    type State = EditorState;
75
76    fn parse_general(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
77        Ok(())
78    }
79
80    fn parse_editor(state: &mut Self::State, line: &str) -> Result<(), Self::Error> {
81        let Ok(KeyValue { key, value }) = KeyValue::parse(line.trim_comment()) else {
82            return Ok(());
83        };
84
85        match key {
86            EditorKey::Bookmarks => {
87                state.bookmarks = value
88                    .split(',')
89                    .map(str::parse)
90                    .filter_map(Result::ok)
91                    .collect();
92            }
93            EditorKey::DistanceSpacing => state.distance_spacing = value.parse_num()?,
94            EditorKey::BeatDivisor => state.beat_divisor = value.parse_num()?,
95            EditorKey::GridSize => state.grid_size = value.parse_num()?,
96            EditorKey::TimelineZoom => state.timeline_zoom = value.parse_num()?,
97        }
98
99        Ok(())
100    }
101
102    fn parse_metadata(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
103        Ok(())
104    }
105
106    fn parse_difficulty(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
107        Ok(())
108    }
109
110    fn parse_events(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
111        Ok(())
112    }
113
114    fn parse_timing_points(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
115        Ok(())
116    }
117
118    fn parse_colors(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
119        Ok(())
120    }
121
122    fn parse_hit_objects(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
123        Ok(())
124    }
125
126    fn parse_variables(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
127        Ok(())
128    }
129
130    fn parse_catch_the_beat(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
131        Ok(())
132    }
133
134    fn parse_mania(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
135        Ok(())
136    }
137}