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