bms_table/lib.rs
1//! 示例程序
2//!
3
4#![warn(missing_docs)]
5
6use anyhow::Result;
7use serde_json::Value;
8use url::Url;
9
10use crate::fetch::{BmsTableHeader, BmsTableParser, CourseInfo, ScoreItem};
11
12pub mod fetch;
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 let parser = BmsTableParser::new();
128
129 // 1. 从HTML页面提取bmstable URL
130 let bmstable_url = parser.extract_bmstable_url(url).await?;
131
132 // 2. 解析bmstable URL为绝对路径
133 let base_url_obj = Url::parse(url)?;
134 let header_url = base_url_obj.join(&bmstable_url)?;
135 let header_url_str = header_url.as_str().to_string();
136
137 // 3. 获取header JSON
138 let header_response = reqwest::Client::new().get(&header_url_str).send().await?;
139 let header_json_content = header_response.text().await?;
140 let header_json: Value = serde_json::from_str(&header_json_content)?;
141
142 // 4. 从header中提取data_url并构建data URL
143 let header: BmsTableHeader = serde_json::from_value(header_json.clone())?;
144 let data_url = header_url.join(&header.data_url)?;
145 let data_url_str = data_url.as_str();
146
147 // 5. 获取data JSON
148 let data_response = reqwest::Client::new().get(data_url_str).send().await?;
149 let data_json_content = data_response.text().await?;
150 let data_json: Value = serde_json::from_str(&data_json_content)?;
151
152 Ok((header_url_str, header_json, data_json))
153}
154
155/// 从URL直接获取BmsTable对象(合并上述两个步骤)
156///
157/// # 参数
158///
159/// * `url` - BMS表格HTML页面的URL
160///
161/// # 返回值
162///
163/// 返回解析后的BmsTable对象
164///
165/// # 错误
166///
167/// 如果无法获取数据或解析失败,将返回错误
168///
169/// # 示例
170///
171/// ```rust,no_run
172/// use bms_table::fetch_bms_table;
173///
174/// #[tokio::main]
175/// async fn main() -> anyhow::Result<()> {
176/// let bms_table = fetch_bms_table("https://example.com/table.html").await?;
177/// println!("表格名称: {}", bms_table.name);
178/// println!("分数数据数量: {}", bms_table.scores.len());
179/// Ok(())
180/// }
181/// ```
182pub async fn fetch_bms_table(url: &str) -> Result<BmsTable> {
183 let (header_url, header_json, data_json) = fetch_table_json_data(url).await?;
184 create_bms_table_from_json(&header_url, header_json, data_json).await
185}