1#![cfg(feature = "serde")]
5
6use anyhow::Result;
7use serde::{Deserialize, Deserializer};
8use serde_json::Value;
9
10use crate::{BmsTableData, BmsTableHeader, ChartItem, CourseInfo, Trophy};
11
12#[derive(Deserialize)]
14struct BmsTableHeaderRaw {
15 name: String,
16 symbol: String,
17 data_url: String,
18 #[serde(default)]
19 course: Option<Value>,
20 #[serde(default)]
21 level_order: Option<Vec<Value>>,
22 #[serde(flatten)]
23 extra: serde_json::Map<String, Value>,
24}
25
26impl TryFrom<BmsTableHeaderRaw> for BmsTableHeader {
29 type Error = String;
30
31 fn try_from(raw: BmsTableHeaderRaw) -> Result<Self, Self::Error> {
32 let course = match raw.course {
33 Some(Value::Array(arr)) => {
34 if arr.is_empty() {
35 vec![Vec::new()]
37 } else if matches!(arr.first(), Some(Value::Array(_))) {
38 serde_json::from_value::<Vec<Vec<CourseInfo>>>(Value::Array(arr))
40 .map_err(|e| e.to_string())?
41 } else {
42 let inner: Vec<CourseInfo> =
44 serde_json::from_value(Value::Array(arr)).map_err(|e| e.to_string())?;
45 vec![inner]
46 }
47 }
48 _ => Vec::new(),
49 };
50
51 let level_order = raw
52 .level_order
53 .unwrap_or_default()
54 .into_iter()
55 .map(|v| match v {
56 Value::Number(n) => n.to_string(),
57 Value::String(s) => s,
58 other => other.to_string(),
59 })
60 .collect::<Vec<String>>();
61
62 Ok(Self {
63 name: raw.name,
64 symbol: raw.symbol,
65 data_url: raw.data_url,
66 course,
67 level_order,
68 extra: Value::Object(raw.extra),
69 })
70 }
71}
72
73impl<'de> Deserialize<'de> for BmsTableHeader {
74 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
75 where
76 D: Deserializer<'de>,
77 {
78 let raw = BmsTableHeaderRaw::deserialize(deserializer)?;
79 Self::try_from(raw).map_err(serde::de::Error::custom)
80 }
81}
82
83#[derive(Deserialize)]
85#[serde(untagged)]
86enum ChartsWrapper {
87 Array(Vec<ChartItem>),
88 Object { charts: Vec<ChartItem> },
89}
90
91impl TryFrom<ChartsWrapper> for BmsTableData {
92 type Error = String;
93
94 fn try_from(w: ChartsWrapper) -> Result<Self, Self::Error> {
95 let charts = match w {
96 ChartsWrapper::Array(v) => v,
97 ChartsWrapper::Object { charts } => charts,
98 };
99 Ok(Self { charts })
100 }
101}
102
103impl<'de> Deserialize<'de> for BmsTableData {
104 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
105 where
106 D: Deserializer<'de>,
107 {
108 let w = ChartsWrapper::deserialize(deserializer)?;
109 Self::try_from(w).map_err(serde::de::Error::custom)
110 }
111}
112
113#[derive(Deserialize)]
115struct CourseInfoRaw {
116 name: String,
117 #[serde(default)]
118 constraint: Vec<String>,
119 #[serde(default)]
120 trophy: Vec<Trophy>,
121 #[serde(default, rename = "md5")]
122 md5list: Vec<String>,
123 #[serde(default, rename = "sha256")]
124 sha256list: Vec<String>,
125 #[serde(default)]
126 charts: Vec<Value>,
127}
128
129impl TryFrom<CourseInfoRaw> for CourseInfo {
130 type Error = String;
131
132 fn try_from(raw: CourseInfoRaw) -> Result<Self, Self::Error> {
133 let mut charts: Vec<ChartItem> =
134 Vec::with_capacity(raw.charts.len() + raw.md5list.len() + raw.sha256list.len());
135
136 for mut chart_value in raw.charts {
138 if chart_value.get("level").is_none() {
139 let obj = chart_value
140 .as_object()
141 .ok_or_else(|| "chart_value is not an object".to_string())?
142 .clone();
143 let mut obj = obj;
144 obj.insert("level".to_string(), Value::String("0".to_string()));
145 chart_value = Value::Object(obj);
146 }
147 let item: ChartItem = serde_json::from_value(chart_value).map_err(|e| e.to_string())?;
148 charts.push(item);
149 }
150
151 charts.extend(raw.md5list.into_iter().map(|md5| ChartItem {
153 level: "0".to_string(),
154 md5: Some(md5),
155 sha256: None,
156 title: None,
157 subtitle: None,
158 artist: None,
159 subartist: None,
160 url: None,
161 url_diff: None,
162 extra: Value::Object(serde_json::Map::new()),
163 }));
164
165 charts.extend(raw.sha256list.into_iter().map(|sha256| ChartItem {
167 level: "0".to_string(),
168 md5: None,
169 sha256: Some(sha256),
170 title: None,
171 subtitle: None,
172 artist: None,
173 subartist: None,
174 url: None,
175 url_diff: None,
176 extra: Value::Object(serde_json::Map::new()),
177 }));
178
179 Ok(Self {
180 name: raw.name,
181 constraint: raw.constraint,
182 trophy: raw.trophy,
183 charts,
184 })
185 }
186}
187
188impl<'de> Deserialize<'de> for CourseInfo {
189 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
190 where
191 D: Deserializer<'de>,
192 {
193 let raw = CourseInfoRaw::deserialize(deserializer)?;
194 Self::try_from(raw).map_err(serde::de::Error::custom)
195 }
196}
197
198fn empty_string_as_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
200where
201 D: Deserializer<'de>,
202{
203 let opt = Option::<String>::deserialize(deserializer)?;
204 Ok(opt.filter(|s| !s.is_empty()))
205}
206
207#[derive(Deserialize)]
209struct ChartItemRaw {
210 level: String,
211 #[serde(default, deserialize_with = "empty_string_as_none")]
212 md5: Option<String>,
213 #[serde(default, deserialize_with = "empty_string_as_none")]
214 sha256: Option<String>,
215 #[serde(default, deserialize_with = "empty_string_as_none")]
216 title: Option<String>,
217 #[serde(default, deserialize_with = "empty_string_as_none")]
218 subtitle: Option<String>,
219 #[serde(default, deserialize_with = "empty_string_as_none")]
220 artist: Option<String>,
221 #[serde(default, deserialize_with = "empty_string_as_none")]
222 subartist: Option<String>,
223 #[serde(default, deserialize_with = "empty_string_as_none")]
224 url: Option<String>,
225 #[serde(default, deserialize_with = "empty_string_as_none")]
226 url_diff: Option<String>,
227 #[serde(flatten)]
228 extra: serde_json::Map<String, Value>,
229}
230
231impl TryFrom<ChartItemRaw> for ChartItem {
232 type Error = String;
233
234 fn try_from(raw: ChartItemRaw) -> Result<Self, Self::Error> {
235 Ok(Self {
236 level: raw.level,
237 md5: raw.md5,
238 sha256: raw.sha256,
239 title: raw.title,
240 subtitle: raw.subtitle,
241 artist: raw.artist,
242 subartist: raw.subartist,
243 url: raw.url,
244 url_diff: raw.url_diff,
245 extra: Value::Object(raw.extra),
246 })
247 }
248}
249
250impl<'de> Deserialize<'de> for ChartItem {
251 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
252 where
253 D: Deserializer<'de>,
254 {
255 let raw = ChartItemRaw::deserialize(deserializer)?;
256 Self::try_from(raw).map_err(serde::de::Error::custom)
257 }
258}