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