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}