chart_file_parser/
song.rs1use std::fmt::Display;
2
3use nom::{bytes::complete::tag, combinator::map_res, multi::many1, sequence::preceded, IResult};
4
5use crate::{
6 components::{curlied, spaced},
7 song_property::SongProperty,
8};
9
10#[derive(Debug, PartialEq, Eq)]
11pub(crate) struct Song<'a> {
12 resolution: u32,
13 properties: Vec<SongProperty<'a>>,
14}
15
16impl<'a> Song<'a> {
17 #[must_use]
18 pub(crate) fn new(resolution: u32, properties: Vec<SongProperty<'a>>) -> Self {
19 Self {
20 resolution,
21 properties,
22 }
23 }
24
25 pub(crate) fn multiply(&mut self, factor: u32) {
26 self.resolution *= factor;
27 }
28
29 #[inline]
30 pub(crate) fn parse(input: &str) -> IResult<&str, Song> {
31 map_res(
32 preceded(
33 spaced(tag("[Song]")),
34 curlied(many1(spaced(SongProperty::parse))),
35 ),
36 Song::try_from,
37 )(input)
38 }
39}
40
41impl<'a> TryFrom<Vec<SongProperty<'a>>> for Song<'a> {
42 type Error = &'static str;
43
44 fn try_from(value: Vec<SongProperty<'a>>) -> Result<Self, Self::Error> {
45 let resolution_entry = value
46 .iter()
47 .find(|x| x.name() == "Resolution")
48 .ok_or("resolution not found")?;
49 let resolution = resolution_entry
50 .value()
51 .parse::<u32>()
52 .map_err(|_| "invalid resolution")?;
53 let properties = value
54 .into_iter()
55 .filter(|x| x.name() != "Resolution")
56 .collect();
57 Ok(Self::new(resolution, properties))
58 }
59}
60
61impl<'a> Display for Song<'a> {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 let resolution_string = self.resolution.to_string();
64 write!(
65 f,
66 "[Song]\n{{\n{}{}}}",
67 SongProperty::new("Resolution", &resolution_string),
68 self.properties
69 .iter()
70 .map(SongProperty::to_string)
71 .collect::<String>()
72 )
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 #![allow(clippy::unwrap_used)]
79 use super::*;
80
81 #[test]
82 fn test_song() {
83 Song::parse(
84 r#"[Song]
85{
86 Name = "Second Sight"
87 Artist = "Adagio"
88 Charter = "Peddy"
89 Album = "Sanctus Ignis"
90 Year = ", 2001"
91 Offset = 0
92 Resolution = 192
93 Player2 = bass
94 Difficulty = 0
95 PreviewStart = 0
96 PreviewEnd = 0
97 Genre = "Neoclassical Metal"
98 MediaType = "cd"
99 MusicStream = "song.ogg"
100}"#,
101 )
102 .unwrap();
103 }
104}