rosu_map/section/
metadata.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 `[Metadata]` section.
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct Metadata {
10    pub title: String,
11    pub title_unicode: String,
12    pub artist: String,
13    pub artist_unicode: String,
14    pub creator: String,
15    pub version: String,
16    pub source: String,
17    pub tags: String,
18    pub beatmap_id: i32,
19    pub beatmap_set_id: i32,
20}
21
22impl Default for Metadata {
23    #[allow(clippy::default_trait_access)]
24    fn default() -> Self {
25        Self {
26            title: Default::default(),
27            title_unicode: Default::default(),
28            artist: Default::default(),
29            artist_unicode: Default::default(),
30            creator: Default::default(),
31            version: Default::default(),
32            source: Default::default(),
33            tags: Default::default(),
34            beatmap_id: -1,
35            beatmap_set_id: Default::default(),
36        }
37    }
38}
39
40impl From<Metadata> for Beatmap {
41    fn from(metadata: Metadata) -> Self {
42        Self {
43            title: metadata.title,
44            title_unicode: metadata.title_unicode,
45            artist: metadata.artist,
46            artist_unicode: metadata.artist_unicode,
47            creator: metadata.creator,
48            version: metadata.version,
49            source: metadata.source,
50            tags: metadata.tags,
51            beatmap_id: metadata.beatmap_id,
52            beatmap_set_id: metadata.beatmap_set_id,
53            ..Self::default()
54        }
55    }
56}
57
58section_keys! {
59    /// All valid keys within a `.osu` file's `[Metadata]` section
60    pub enum MetadataKey {
61        Title,
62        TitleUnicode,
63        Artist,
64        ArtistUnicode,
65        Creator,
66        Version,
67        Source,
68        Tags,
69        BeatmapID,
70        BeatmapSetID,
71    }
72}
73
74thiserror! {
75    /// All the ways that parsing a `.osu` file into [`Metadata`] can fail.
76    #[derive(Debug)]
77    pub enum ParseMetadataError {
78        #[error("failed to parse number")]
79        Number(#[from] ParseNumberError),
80    }
81}
82
83/// The parsing state for [`Metadata`] in [`DecodeBeatmap`].
84pub type MetadataState = Metadata;
85
86impl DecodeState for MetadataState {
87    fn create(_: i32) -> Self {
88        Self::default()
89    }
90}
91
92impl DecodeBeatmap for Metadata {
93    type Error = ParseMetadataError;
94    type State = Self;
95
96    fn parse_general(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
97        Ok(())
98    }
99
100    fn parse_editor(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
101        Ok(())
102    }
103
104    fn parse_metadata(state: &mut Self::State, line: &str) -> Result<(), Self::Error> {
105        let Ok(KeyValue { key, value }) = KeyValue::parse(line) else {
106            return Ok(());
107        };
108
109        match key {
110            MetadataKey::Title => value.clone_into(&mut state.title),
111            MetadataKey::TitleUnicode => value.clone_into(&mut state.title_unicode),
112            MetadataKey::Artist => value.clone_into(&mut state.artist),
113            MetadataKey::ArtistUnicode => value.clone_into(&mut state.artist_unicode),
114            MetadataKey::Creator => value.clone_into(&mut state.creator),
115            MetadataKey::Version => value.clone_into(&mut state.version),
116            MetadataKey::Source => value.clone_into(&mut state.source),
117            MetadataKey::Tags => value.clone_into(&mut state.tags),
118            MetadataKey::BeatmapID => state.beatmap_id = value.parse_num()?,
119            MetadataKey::BeatmapSetID => state.beatmap_set_id = value.parse_num()?,
120        }
121
122        Ok(())
123    }
124
125    fn parse_difficulty(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
126        Ok(())
127    }
128
129    fn parse_events(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
130        Ok(())
131    }
132
133    fn parse_timing_points(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
134        Ok(())
135    }
136
137    fn parse_colors(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
138        Ok(())
139    }
140
141    fn parse_hit_objects(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
142        Ok(())
143    }
144
145    fn parse_variables(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
146        Ok(())
147    }
148
149    fn parse_catch_the_beat(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
150        Ok(())
151    }
152
153    fn parse_mania(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
154        Ok(())
155    }
156}