osu_file_parser/osu_file/events/audio_sample/
mod.rs

1pub mod error;
2
3pub use error::*;
4use nom::{
5    branch::alt,
6    bytes::complete::tag,
7    combinator::{complete, eof, map_res},
8    error::context,
9    sequence::{preceded, tuple},
10    Parser,
11};
12
13use crate::{
14    osu_file::{
15        FilePath, Integer, InvalidRepr, Version, VersionedDefault, VersionedFrom,
16        VersionedFromRepr, VersionedFromStr, VersionedToString, VersionedTryFrom,
17    },
18    parsers::{comma, comma_field, comma_field_type},
19};
20
21#[derive(Clone, Debug, PartialEq, Eq, Hash)]
22pub struct AudioSample {
23    pub time: Integer,
24    pub layer: Layer,
25    pub filepath: FilePath,
26    pub volume: Volume,
27}
28
29impl VersionedFromStr for AudioSample {
30    type Err = ParseAudioSampleError;
31
32    fn from_str(s: &str, version: Version) -> Result<Option<Self>, Self::Err> {
33        if s.is_empty() {
34            return Err(ParseAudioSampleError::WrongEvent);
35        }
36
37        let header = context(ParseAudioSampleError::WrongEvent.into(), tag("Sample"));
38        let time = context(
39            ParseAudioSampleError::InvalidTime.into(),
40            comma_field_type(),
41        );
42        let layer = context(
43            ParseAudioSampleError::InvalidLayer.into(),
44            map_res(
45                context(
46                    ParseAudioSampleError::InvalidLayer.into(),
47                    comma_field_type(),
48                ),
49                // for now all of those unwraps are safe, can change on the future versions though
50                |layer| Layer::from_repr(layer, version).map(|layer| layer.unwrap()),
51            ),
52        );
53        let filepath = comma_field().map(|p| p.into());
54        let volume = alt((
55            eof.map(|_| Volume::default(version).unwrap()),
56            preceded(
57                context(ParseAudioSampleError::MissingVolume.into(), comma()),
58                context(
59                    ParseAudioSampleError::InvalidVolume.into(),
60                    map_res::<_, _, _, _, ParseVolumeError, _, _>(comma_field(), |s| {
61                        Ok(Volume::from_str(s, version)?.unwrap())
62                    }),
63                ),
64            ),
65        ));
66
67        let (_, (time, layer, filepath, volume)) = tuple((
68            preceded(
69                tuple((
70                    complete(header),
71                    context(ParseAudioSampleError::MissingTime.into(), comma()),
72                )),
73                time,
74            ),
75            preceded(
76                context(ParseAudioSampleError::MissingLayer.into(), comma()),
77                layer,
78            ),
79            preceded(
80                context(ParseAudioSampleError::MissingFilepath.into(), comma()),
81                filepath,
82            ),
83            volume,
84        ))(s)?;
85
86        Ok(Some(AudioSample {
87            time,
88            layer,
89            filepath,
90            volume,
91        }))
92    }
93}
94
95impl VersionedToString for AudioSample {
96    fn to_string(&self, version: Version) -> Option<String> {
97        Some(format!(
98            "Sample,{},{},{},{}",
99            self.time,
100            self.layer as usize,
101            self.filepath.to_string(version).unwrap(),
102            self.volume.to_string(version).unwrap()
103        ))
104    }
105}
106
107#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
108pub struct Volume(u8);
109
110impl VersionedDefault for Volume {
111    fn default(_: Version) -> Option<Self> {
112        Some(Volume(100))
113    }
114}
115
116impl Volume {
117    pub fn new(volume: u8, version: Version) -> Result<Volume, VolumeSetError> {
118        <Volume as VersionedTryFrom<u8>>::try_from(volume, version).map(|volume| volume.unwrap())
119    }
120
121    pub fn get(&self) -> u8 {
122        self.0
123    }
124
125    pub fn set(&mut self, value: u8, version: Version) -> Result<(), VolumeSetError> {
126        *self = <Volume as VersionedTryFrom<u8>>::try_from(value, version)
127            .map(|volume| volume.unwrap())?;
128
129        Ok(())
130    }
131}
132
133impl VersionedTryFrom<u8> for Volume {
134    type Error = VolumeSetError;
135
136    fn try_from(value: u8, _: Version) -> Result<Option<Self>, Self::Error> {
137        if value > 100 {
138            Err(VolumeSetError::VolumeTooHigh)
139        } else {
140            Ok(Some(Volume(value)))
141        }
142    }
143}
144
145impl VersionedFrom<Volume> for u8 {
146    fn from(volume: Volume, _: Version) -> Option<Self> {
147        Some(volume.get())
148    }
149}
150
151impl VersionedFromStr for Volume {
152    type Err = ParseVolumeError;
153
154    fn from_str(s: &str, version: Version) -> Result<Option<Self>, Self::Err> {
155        <Volume as VersionedTryFrom<u8>>::try_from(s.parse::<u8>()?, version)
156            .map_err(|err| err.into())
157    }
158}
159
160impl VersionedToString for Volume {
161    fn to_string(&self, version: Version) -> Option<String> {
162        <u8 as VersionedFrom<Volume>>::from(*self, version).map(|volume| volume.to_string())
163    }
164}
165
166#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
167#[non_exhaustive]
168pub enum Layer {
169    Background,
170    Fail,
171    Pass,
172    Foreground,
173}
174
175impl VersionedFromRepr for Layer {
176    fn from_repr(repr: usize, _: Version) -> Result<Option<Self>, InvalidRepr> {
177        match repr {
178            0 => Ok(Some(Layer::Background)),
179            1 => Ok(Some(Layer::Fail)),
180            2 => Ok(Some(Layer::Pass)),
181            3 => Ok(Some(Layer::Foreground)),
182            _ => Err(InvalidRepr),
183        }
184    }
185}