bms_table/
lib.rs

1//! BMS 难度表数据获取与解析库
2//!
3//! 提供从网页或 JSON 源构建完整的 BMS 难度表数据结构,涵盖表头、课程、奖杯与谱面条目等。
4//! 结合可选特性实现网络抓取与 HTML 解析,适用于 CLI 工具、服务端程序或数据处理流水线。
5//!
6//! # 功能概述
7//!
8//! - 解析表头 JSON,支持收集未识别的额外字段
9//! - 解析谱面数据,兼容数组或 `{ charts: [...] }` 两种格式
10//! - 课程数据支持从 `md5`/`sha256` 列表自动转换为谱面条目
11//! - 可选特性 `reqwest` 提供一站式网络获取接口
12//! - 可选特性 `scraper` 支持从 HTML `<meta name="bmstable">` 提取头部 JSON 地址
13//!
14//! # 快速上手
15//!
16//! ```rust,no_run
17//! # #[tokio::main]
18//! # async fn main() -> anyhow::Result<()> {
19//! use bms_table::fetch::reqwest::fetch_bms_table;
20//!
21//! let table = fetch_bms_table("https://stellabms.xyz/sl/table.html").await?;
22//! println!("{}: {} charts", table.header.name, table.data.charts.len());
23//! # Ok(())
24//! # }
25//! ```
26//!
27//! # 特性说明
28//!
29//! - `reqwest`:启用网络获取功能(默认启用)
30//! - `scraper`:启用 HTML 解析(用于从页面提取 bmstable 头部地址)
31
32#![warn(missing_docs)]
33#![warn(clippy::must_use_candidate)]
34#![deny(rustdoc::broken_intra_doc_links)]
35#![cfg_attr(docsrs, feature(doc_cfg))]
36
37pub mod de;
38pub mod fetch;
39pub mod ser;
40
41use serde::{Deserialize, Serialize};
42use serde_json::Value;
43
44/// 顶层 BMS 难度表数据结构。
45///
46/// 将表头元数据与谱面数据打包在一起,便于在应用中一次性传递与使用。
47#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
48pub struct BmsTable {
49    /// 表头信息与额外字段
50    pub header: BmsTableHeader,
51    /// 表数据,包含谱面列表
52    pub data: BmsTableData,
53}
54
55/// BMS 表头信息。
56///
57/// 该结构严格解析常见字段,并把未识别的字段保存在 `extra` 中,保证向前兼容。
58#[derive(Debug, Clone, PartialEq)]
59pub struct BmsTableHeader {
60    /// 表格名称,如 "Satellite"
61    pub name: String,
62    /// 表格符号,如 "sl"
63    pub symbol: String,
64    /// 谱面数据文件的URL(原样保存来自header JSON的字符串)
65    pub data_url: String,
66    /// 课程信息数组,每个元素是一个课程组的数组
67    pub course: Vec<Vec<CourseInfo>>,
68    /// 难度等级顺序,包含数字和字符串
69    pub level_order: Vec<String>,
70    /// 额外数据(来自header JSON中未识别的字段)
71    pub extra: Value,
72}
73
74/// BMS 表数据。
75///
76/// 仅包含谱面数组。解析时同时兼容纯数组与 `{ charts: [...] }` 两种输入形式。
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct BmsTableData {
79    /// 谱面数据
80    pub charts: Vec<ChartItem>,
81}
82
83/// 课程信息。
84///
85/// 描述一个课程的名称、约束、奖杯与谱面集合。解析阶段会将 `md5`/`sha256`
86/// 列表自动转换为对应的 `ChartItem`,并为缺失 `level` 的谱面补充默认值 `"0"`。
87#[derive(Debug, Clone, PartialEq, Serialize)]
88pub struct CourseInfo {
89    /// 课程名称,如 "Satellite Skill Analyzer 2nd sl0"
90    pub name: String,
91    /// 约束条件列表,如 ["grade_mirror", "gauge_lr2", "ln"]
92    #[serde(default)]
93    pub constraint: Vec<String>,
94    /// 奖杯信息列表,定义不同等级的奖杯要求
95    #[serde(default)]
96    pub trophy: Vec<Trophy>,
97    /// 谱面数据列表,包含该课程的所有谱面信息
98    #[serde(default)]
99    pub charts: Vec<ChartItem>,
100}
101
102/// 谱面数据项。
103///
104/// 描述单个 BMS 文件的相关元数据与资源链接。为空字符串的可选字段在反序列化时会
105/// 自动转换为 `None`,以提升数据质量。
106#[derive(Debug, Clone, PartialEq, Eq)]
107pub struct ChartItem {
108    /// 难度等级,如 "0"
109    pub level: String,
110    /// 文件的MD5哈希值
111    pub md5: Option<String>,
112    /// 文件的SHA256哈希值
113    pub sha256: Option<String>,
114    /// 歌曲标题
115    pub title: Option<String>,
116    /// 歌曲副标题
117    pub subtitle: Option<String>,
118    /// 艺术家名称
119    pub artist: Option<String>,
120    /// 歌曲副艺术家
121    pub subartist: Option<String>,
122    /// 文件下载链接
123    pub url: Option<String>,
124    /// 差分文件下载链接(可选)
125    pub url_diff: Option<String>,
126    /// 额外数据
127    pub extra: Value,
128}
129
130/// 奖杯信息。
131///
132/// 定义达成特定奖杯的条件,包括最大 miss 率与最低得分率等要求。
133#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
134pub struct Trophy {
135    /// 奖杯名称,如 "silvermedal" 或 "goldmedal"
136    pub name: String,
137    /// 最大miss率(百分比),如 5.0 表示最大5%的miss率
138    pub missrate: f64,
139    /// 最小得分率(百分比),如 70.0 表示至少70%的得分率
140    pub scorerate: f64,
141}