rosu_map/section/colors/
decode.rs

1use std::{num::ParseIntError, str::FromStr};
2
3use crate::{
4    decode::{DecodeBeatmap, DecodeState},
5    section::UnknownKeyError,
6    util::{KeyValue, ParseNumberError, StrExt},
7    Beatmap,
8};
9
10use super::{Color, CustomColor};
11
12/// Struct containing all data from a `.osu` file's `[Colours]` section.
13#[derive(Clone, Debug, Default, PartialEq, Eq)]
14pub struct Colors {
15    pub custom_combo_colors: Vec<Color>,
16    pub custom_colors: Vec<CustomColor>,
17}
18
19impl From<Colors> for Beatmap {
20    fn from(colors: Colors) -> Self {
21        Self {
22            custom_combo_colors: colors.custom_combo_colors,
23            custom_colors: colors.custom_colors,
24            ..Self::default()
25        }
26    }
27}
28
29impl Colors {
30    pub const DEFAULT_COMBO_COLORS: [Color; 4] = [
31        Color([255, 192, 0, 255]),
32        Color([0, 202, 0, 255]),
33        Color([18, 124, 255, 255]),
34        Color([242, 24, 57, 255]),
35    ];
36}
37
38/// All valid keys within a `.osu` file's `[Colours]` section
39#[derive(Clone, Debug, PartialEq, Eq)]
40pub enum ColorsKey {
41    Combo,
42    Name(String),
43}
44
45impl FromStr for ColorsKey {
46    type Err = UnknownKeyError;
47
48    fn from_str(s: &str) -> Result<Self, Self::Err> {
49        if s.starts_with("Combo") {
50            Ok(Self::Combo)
51        } else {
52            Ok(Self::Name(s.to_owned()))
53        }
54    }
55}
56
57thiserror! {
58    /// All the ways that parsing a `.osu` file into [`Colors`] can fail.
59    #[derive(Debug)]
60    pub enum ParseColorsError {
61        #[error("color specified in incorrect format (should be R,G,B or R,G,B,A)")]
62        IncorrectColor,
63        #[error("failed to parse number")]
64        Number(#[source] ParseNumberError),
65    }
66}
67
68impl From<ParseIntError> for ParseColorsError {
69    fn from(err: ParseIntError) -> Self {
70        Self::Number(ParseNumberError::InvalidInteger(err))
71    }
72}
73
74/// The parsing state for [`Colors`] in [`DecodeBeatmap`].
75pub type ColorsState = Colors;
76
77impl DecodeState for ColorsState {
78    fn create(_version: i32) -> Self {
79        Self::default()
80    }
81}
82
83impl DecodeBeatmap for Colors {
84    type Error = ParseColorsError;
85    type State = ColorsState;
86
87    fn parse_general(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
88        Ok(())
89    }
90
91    fn parse_editor(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
92        Ok(())
93    }
94
95    fn parse_metadata(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
96        Ok(())
97    }
98
99    fn parse_difficulty(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
100        Ok(())
101    }
102
103    fn parse_events(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
104        Ok(())
105    }
106
107    fn parse_timing_points(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
108        Ok(())
109    }
110
111    fn parse_colors(state: &mut Self::State, line: &str) -> Result<(), Self::Error> {
112        let Ok(KeyValue { key, value }) = KeyValue::parse(line.trim_comment()) else {
113            return Ok(());
114        };
115
116        let color: Color = value.parse()?;
117
118        match key {
119            ColorsKey::Combo => state.custom_combo_colors.push(color),
120            ColorsKey::Name(name) => {
121                match state.custom_colors.iter_mut().find(|c| c.name == name) {
122                    Some(old) => old.color = color,
123                    None => state.custom_colors.push(CustomColor { name, color }),
124                }
125            }
126        }
127
128        Ok(())
129    }
130
131    fn parse_hit_objects(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
132        Ok(())
133    }
134
135    fn parse_variables(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
136        Ok(())
137    }
138
139    fn parse_catch_the_beat(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
140        Ok(())
141    }
142
143    fn parse_mania(_: &mut Self::State, _: &str) -> Result<(), Self::Error> {
144        Ok(())
145    }
146}