rgc_chart/models/osu/
metadata.rs1use std::str::FromStr;
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, PartialEq)]
5pub struct Metadata {
6 pub title: String,
7
8 pub title_unicode: String,
9
10 pub artist: String,
11
12 pub artist_unicode: String,
13
14 pub creator: String,
15
16 pub version: String,
17
18 pub source: String,
19
20 pub tags: Vec<String>,
21
22 pub beatmap_id: i32,
23
24 pub beatmap_set_id: i32,
25}
26
27impl Default for Metadata {
28 fn default() -> Self {
29 Self {
30 title: String::new(),
31 title_unicode: String::new(),
32 artist: String::new(),
33 artist_unicode: String::new(),
34 creator: String::new(),
35 version: String::new(),
36 source: String::new(),
37 tags: Vec::new(),
38 beatmap_id: 0,
39 beatmap_set_id: 0,
40 }
41 }
42}
43
44impl FromStr for Metadata {
45 type Err = String;
46
47 fn from_str(s: &str) -> Result<Self, Self::Err> {
48 let mut metadata = Metadata::default();
49
50 let mut key_values = HashMap::new();
51 for line in s.lines() {
52 let line = line.trim();
53 if line.is_empty() || line.starts_with("//") {
54 continue;
55 }
56
57 if let Some((key, value)) = line.split_once(':') {
58 let key = key.trim();
59 let value = value.trim();
60 key_values.insert(key.to_string(), value.to_string());
61 }
62 }
63
64 if let Some(value) = key_values.get("Title") {
65 metadata.title = value.clone();
66 }
67
68 if let Some(value) = key_values.get("TitleUnicode") {
69 metadata.title_unicode = value.clone();
70 }
71
72 if let Some(value) = key_values.get("Artist") {
73 metadata.artist = value.clone();
74 }
75
76 if let Some(value) = key_values.get("ArtistUnicode") {
77 metadata.artist_unicode = value.clone();
78 }
79
80 if let Some(value) = key_values.get("Creator") {
81 metadata.creator = value.clone();
82 }
83
84 if let Some(value) = key_values.get("Version") {
85 metadata.version = value.clone();
86 }
87
88 if let Some(value) = key_values.get("Source") {
89 metadata.source = value.clone();
90 }
91
92 if let Some(value) = key_values.get("Tags") {
93 metadata.tags = value
94 .split_whitespace()
95 .map(|tag| tag.to_string())
96 .collect();
97 }
98
99 if let Some(value) = key_values.get("BeatmapID") {
100 metadata.beatmap_id = value.parse::<i32>()
101 .map_err(|_| format!("Invalid BeatmapID value: {}", value))?;
102 }
103
104 if let Some(value) = key_values.get("BeatmapSetID") {
105 metadata.beatmap_set_id = value.parse::<i32>()
106 .map_err(|_| format!("Invalid BeatmapSetID value: {}", value))?;
107 }
108
109 Ok(metadata)
110 }
111}
112
113impl Metadata {
114 pub fn new(
115 title: String,
116 title_unicode: String,
117 artist: String,
118 artist_unicode: String,
119 creator: String,
120 version: String,
121 source: String,
122 tags: Vec<String>,
123 beatmap_id: i32,
124 beatmap_set_id: i32,
125 ) -> Self {
126 Self {
127 title,
128 title_unicode,
129 artist,
130 artist_unicode,
131 creator,
132 version,
133 source,
134 tags,
135 beatmap_id,
136 beatmap_set_id,
137 }
138 }
139
140 pub fn display_title(&self) -> &str {
141 if !self.title_unicode.is_empty() {
142 &self.title_unicode
143 } else {
144 &self.title
145 }
146 }
147
148 pub fn display_artist(&self) -> &str {
149 if !self.artist_unicode.is_empty() {
150 &self.artist_unicode
151 } else {
152 &self.artist
153 }
154 }
155
156 pub fn add_tag(&mut self, tag: String) {
157 if !self.tags.contains(&tag) {
158 self.tags.push(tag);
159 }
160 }
161
162 pub fn remove_tag(&mut self, tag: &str) {
163 self.tags.retain(|t| t != tag);
164 }
165
166 pub fn has_tag(&self, tag: &str) -> bool {
167 self.tags.iter().any(|t| t.eq_ignore_ascii_case(tag))
168 }
169
170 pub fn tags_string(&self) -> String {
171 self.tags.join(" ")
172 }
173
174 pub fn set_tags_from_string(&mut self, tags_str: &str) {
175 self.tags = tags_str
176 .split_whitespace()
177 .map(|tag| tag.to_string())
178 .collect();
179 }
180
181 pub fn is_submitted(&self) -> bool {
182 self.beatmap_id > 0 && self.beatmap_set_id > 0
183 }
184
185 pub fn has_unicode(&self) -> bool {
186 !self.title_unicode.is_empty() || !self.artist_unicode.is_empty()
187 }
188
189 pub fn to_osu_format(&self) -> String {
190 let mut lines = Vec::new();
191
192 lines.push(format!("Title: {}", self.title));
193 lines.push(format!("TitleUnicode: {}", self.title_unicode));
194 lines.push(format!("Artist: {}", self.artist));
195 lines.push(format!("ArtistUnicode: {}", self.artist_unicode));
196 lines.push(format!("Creator: {}", self.creator));
197 lines.push(format!("Version: {}", self.version));
198 lines.push(format!("Source: {}", self.source));
199 lines.push(format!("Tags: {}", self.tags_string()));
200 lines.push(format!("BeatmapID: {}", self.beatmap_id));
201 lines.push(format!("BeatmapSetID: {}", self.beatmap_set_id));
202
203 lines.join("\n")
204 }
205}