bms_table/lib.rs
1//! 示例程序
2//!
3
4#![warn(missing_docs)]
5
6mod fetch;
7
8use anyhow::Result;
9use serde_json::Value;
10use url::Url;
11
12use crate::fetch::{extract_bmstable_url, BmsTableHeader, CourseInfo, ScoreItem};
13
14/// BMS难度表数据,看这一个就够了
15#[derive(Debug, Clone, PartialEq)]
16pub struct BmsTable {
17 /// 表格名称,如 "Satellite"
18 pub name: String,
19 /// 表格符号,如 "sl"
20 pub symbol: String,
21 /// 表格头文件的相对URL,如 "header.json"
22 pub header_url: Url,
23 /// 分数数据文件的相对URL,如 "score.json"
24 pub data_url: Url,
25 /// 课程信息数组,每个元素是一个课程组的数组
26 pub course: Vec<Vec<CourseInfo>>,
27 /// 分数数据
28 pub scores: Vec<ScoreItem>,
29}
30
31/// 从header的绝对URL地址、header和data的JSON解析树创建BmsTable对象
32///
33/// # 参数
34///
35/// * `header_url` - header文件的绝对URL地址
36/// * `header_json` - header的JSON解析树
37/// * `data_json` - data的JSON解析树
38///
39/// # 返回值
40///
41/// 返回解析后的BmsTable对象
42///
43/// # 错误
44///
45/// 如果JSON解析失败或URL解析失败,将返回错误
46///
47/// # 示例
48///
49/// ```rust,no_run
50/// use bms_table::{create_bms_table_from_json, BmsTable};
51/// use serde_json::json;
52/// use url::Url;
53///
54/// #[tokio::main]
55/// async fn main() -> anyhow::Result<()> {
56/// let header_url = "https://example.com/header.json";
57/// let header_json = json!({
58/// "name": "Test Table",
59/// "symbol": "test",
60/// "data_url": "score.json",
61/// "course": []
62/// });
63/// let data_json = json!([]);
64///
65/// let bms_table = create_bms_table_from_json(header_url, header_json, data_json).await?;
66/// println!("表格名称: {}", bms_table.name);
67/// Ok(())
68/// }
69/// ```
70pub async fn create_bms_table_from_json(
71 header_url: &str,
72 header_json: Value,
73 data_json: Value,
74) -> Result<BmsTable> {
75 // 解析header JSON
76 let header: BmsTableHeader = serde_json::from_value(header_json)?;
77
78 // 解析data JSON
79 let scores: Vec<ScoreItem> = serde_json::from_value(data_json)?;
80
81 // 构建URL对象
82 let header_url_obj = Url::parse(header_url)?;
83 let data_url_obj = header_url_obj.join(&header.data_url)?;
84
85 // 创建BmsTable对象
86 let bms_table = BmsTable {
87 name: header.name,
88 symbol: header.symbol,
89 header_url: header_url_obj,
90 data_url: data_url_obj,
91 course: header.course,
92 scores,
93 };
94
95 Ok(bms_table)
96}
97
98/// 从URL获取header的绝对URL地址、header和data的JSON解析树
99///
100/// # 参数
101///
102/// * `url` - BMS表格HTML页面的URL
103///
104/// # 返回值
105///
106/// 返回一个元组,包含header的绝对URL地址、header的JSON解析树和data的JSON解析树
107///
108/// # 错误
109///
110/// 如果无法获取数据或解析失败,将返回错误
111///
112/// # 示例
113///
114/// ```rust,no_run
115/// use bms_table::fetch_table_json_data;
116///
117/// #[tokio::main]
118/// async fn main() -> anyhow::Result<()> {
119/// let (header_url, header_json, data_json) = fetch_table_json_data("https://example.com/table.html").await?;
120/// println!("Header URL: {}", header_url);
121/// println!("Header JSON: {:?}", header_json);
122/// println!("Data JSON: {:?}", data_json);
123/// Ok(())
124/// }
125/// ```
126pub async fn fetch_table_json_data(url: &str) -> Result<(String, Value, Value)> {
127 // 1. 从HTML页面提取bmstable URL
128 let bmstable_url = extract_bmstable_url(url).await?;
129
130 // 2. 解析bmstable URL为绝对路径
131 let base_url_obj = Url::parse(url)?;
132 let header_url = base_url_obj.join(&bmstable_url)?;
133 let header_url_str = header_url.as_str().to_string();
134
135 // 3. 获取header JSON
136 let header_response = reqwest::Client::new().get(&header_url_str).send().await?;
137 let header_json_content = header_response.text().await?;
138 let header_json: Value = serde_json::from_str(&header_json_content)?;
139
140 // 4. 从header中提取data_url并构建data URL
141 let header: BmsTableHeader = serde_json::from_value(header_json.clone())?;
142 let data_url = header_url.join(&header.data_url)?;
143 let data_url_str = data_url.as_str();
144
145 // 5. 获取data JSON
146 let data_response = reqwest::Client::new().get(data_url_str).send().await?;
147 let data_json_content = data_response.text().await?;
148 let data_json: Value = serde_json::from_str(&data_json_content)?;
149
150 Ok((header_url_str, header_json, data_json))
151}
152
153/// 从URL直接获取BmsTable对象(合并上述两个步骤)
154///
155/// # 参数
156///
157/// * `url` - BMS表格HTML页面的URL
158///
159/// # 返回值
160///
161/// 返回解析后的BmsTable对象
162///
163/// # 错误
164///
165/// 如果无法获取数据或解析失败,将返回错误
166///
167/// # 示例
168///
169/// ```rust,no_run
170/// use bms_table::fetch_bms_table;
171///
172/// #[tokio::main]
173/// async fn main() -> anyhow::Result<()> {
174/// let bms_table = fetch_bms_table("https://example.com/table.html").await?;
175/// println!("表格名称: {}", bms_table.name);
176/// println!("分数数据数量: {}", bms_table.scores.len());
177/// Ok(())
178/// }
179/// ```
180pub async fn fetch_bms_table(url: &str) -> Result<BmsTable> {
181 let (header_url, header_json, data_json) = fetch_table_json_data(url).await?;
182 create_bms_table_from_json(&header_url, header_json, data_json).await
183}