osu_file_parser/osu_file/osb/
mod.rs

1pub mod error;
2pub mod types;
3
4use nom::multi::many0;
5
6use crate::parsers::square_section;
7
8use super::{Error, Events, Version, VersionedFromStr, VersionedToString};
9
10pub use error::*;
11pub use types::*;
12
13#[derive(Clone, Debug, Hash, PartialEq, Eq)]
14pub struct Osb {
15    pub variables: Option<Vec<Variable>>,
16    pub events: Option<Events>,
17}
18
19impl VersionedFromStr for Osb {
20    type Err = Error<ParseError>;
21
22    fn from_str(s: &str, version: Version) -> std::result::Result<Option<Self>, Self::Err> {
23        if version < 14 {
24            return Ok(None);
25        }
26
27        let pre_section_count = s
28            .lines()
29            .take_while(|s| {
30                let s = s.trim();
31                !s.trim().starts_with('[') && !s.trim().ends_with(']')
32            })
33            .count();
34
35        for (i, line) in s.lines().take(pre_section_count).enumerate() {
36            let line = line.trim();
37
38            if line.is_empty() {
39                continue;
40            }
41
42            if line.starts_with("//") {
43                continue;
44            }
45
46            return Err(Error::new(ParseError::UnexpectedLine, i));
47        }
48
49        let s = s
50            .lines()
51            .skip(pre_section_count)
52            .collect::<Vec<_>>()
53            .join("\n");
54
55        // we get sections
56        // only valid sections currently are [Variables] [Events]
57        let (_, sections) = many0(square_section())(&s).unwrap();
58
59        let mut section_parsed = Vec::with_capacity(2);
60        let mut line_number = pre_section_count;
61
62        let (mut events, mut variables) = (None, None);
63
64        for (ws, section_name, ws2, section) in sections {
65            line_number += ws.lines().count();
66
67            if section_parsed.contains(&section_name) {
68                return Err(Error::new(ParseError::DuplicateSections, line_number));
69            }
70
71            let section_name_line = line_number;
72            line_number += ws2.lines().count();
73
74            match section_name {
75                "Variables" => {
76                    let mut vars = Vec::new();
77                    for (i, line) in section.lines().enumerate() {
78                        if line.trim().is_empty() {
79                            continue;
80                        }
81
82                        let variable = Error::new_from_result_into(
83                            Variable::from_str(line, version).map(|v| v.unwrap()),
84                            line_number + i,
85                        )?;
86
87                        vars.push(variable);
88                    }
89                    variables = Some(vars);
90                }
91                "Events" => {
92                    events = Error::processing_line(
93                        Events::from_str_variables(
94                            section,
95                            version,
96                            variables.as_ref().unwrap_or(&Vec::new()),
97                        ),
98                        line_number,
99                    )?;
100                }
101                _ => return Err(Error::new(ParseError::UnknownSection, section_name_line)),
102            }
103
104            section_parsed.push(section_name);
105            line_number += section.lines().count().saturating_sub(1);
106        }
107
108        Ok(Some(Osb { events, variables }))
109    }
110}
111
112impl VersionedToString for Osb {
113    fn to_string(&self, version: Version) -> Option<String> {
114        if version < 14 {
115            None
116        } else {
117            let mut sections = Vec::new();
118
119            if let Some(variables) = &self.variables {
120                sections.push(format!(
121                    "[Variables]\n{}",
122                    variables
123                        .iter()
124                        .map(|v| v.to_string(version).unwrap())
125                        .collect::<Vec<_>>()
126                        .join("\n"),
127                ));
128            }
129            if let Some(events) = &self.events {
130                // Events existed longer than storyboards I think
131                sections.push(format!(
132                    "[Events]\n{}",
133                    events
134                        .to_string_variables(
135                            version,
136                            self.variables.as_ref().unwrap_or(&Vec::new()),
137                        )
138                        .unwrap()
139                ))
140            }
141
142            Some(sections.join("\n\n"))
143        }
144    }
145}