Skip to content

Commit 4b15677

Browse files
committed
refactor: 统一配置键名格式
- 将 Rust snake_case 字段自动序列化为 PascalCase - 读取时忽略大小写和下划线以兼容旧配置 - 将 ConfigVersion 与 Enable 纳入统一命名规则 - 删除可由 Rust 字段名自动推导的冗余 key 声明
1 parent 5ca5231 commit 4b15677

17 files changed

Lines changed: 138 additions & 113 deletions

File tree

packages/applechu-schema/src/lib.rs

Lines changed: 75 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,38 @@ pub const DEFAULT_CONFIG_HEADER: &str = r#"## 这是 AppleChu 的 TOML 配置文
1111
##
1212
## - 井号 # 开头的行为注释,被注释掉的内容不会生效
1313
## - 被注释的配置内容使用一个井号 #,说明文字使用两个井号 ##
14-
## - 功能开关统一使用 enable = true/false
14+
## - 功能开关统一使用 Enable = true/false
1515
## - 未填写的配置使用程序默认值
1616
17-
config_version = 1
17+
ConfigVersion = 1
1818
"#;
1919

20+
pub fn canonical_key(key: &str) -> String {
21+
let mut output = String::with_capacity(key.len());
22+
let mut capitalize = true;
23+
for character in key.chars() {
24+
if character == '_' {
25+
capitalize = true;
26+
} else if capitalize {
27+
output.extend(character.to_uppercase());
28+
capitalize = false;
29+
} else {
30+
output.push(character);
31+
}
32+
}
33+
output
34+
}
35+
36+
pub fn keys_equal(left: &str, right: &str) -> bool {
37+
left.bytes()
38+
.filter(|byte| *byte != b'_')
39+
.map(|byte| byte.to_ascii_lowercase())
40+
.eq(right
41+
.bytes()
42+
.filter(|byte| *byte != b'_')
43+
.map(|byte| byte.to_ascii_lowercase()))
44+
}
45+
2046
#[derive(Clone, Debug)]
2147
pub struct OptionSpec {
2248
pub value: toml::Value,
@@ -190,11 +216,12 @@ impl Schema {
190216
.and_then(toml::Value::as_bool)
191217
.unwrap_or(false);
192218
let mut entries = parse_entries(table, id)?;
219+
for entry in &mut entries {
220+
entry.key = canonical_key(&entry.key);
221+
}
193222
if !hidden
194223
&& !always_enabled
195-
&& !entries
196-
.iter()
197-
.any(|entry| entry.key.eq_ignore_ascii_case("enable"))
224+
&& !entries.iter().any(|entry| keys_equal(&entry.key, "enable"))
198225
{
199226
entries.insert(0, enable_entry(default_on));
200227
}
@@ -213,6 +240,7 @@ impl Schema {
213240
validate_sections(&sections)?;
214241
validate_groups(root, &sections)?;
215242
inject_enable_entries(&mut document, &sections)?;
243+
canonicalize_document_keys(&mut document)?;
216244
Ok(Self {
217245
source,
218246
document,
@@ -231,15 +259,15 @@ impl Schema {
231259
pub fn section(&self, id: &str) -> Option<&SectionSpec> {
232260
self.sections
233261
.iter()
234-
.find(|section| section.id.eq_ignore_ascii_case(id))
262+
.find(|section| keys_equal(&section.id, id))
235263
}
236264

237265
pub fn entry(&self, section: &str, key: &str) -> Option<&EntrySpec> {
238266
self.section(section).and_then(|section| {
239267
section
240268
.entries
241269
.iter()
242-
.find(|entry| entry.key.eq_ignore_ascii_case(key))
270+
.find(|entry| keys_equal(&entry.key, key))
243271
})
244272
}
245273

@@ -268,7 +296,7 @@ impl Schema {
268296
if !entry.emit_default {
269297
output.push('#');
270298
}
271-
output.push_str(&entry.key);
299+
output.push_str(&canonical_key(&entry.key));
272300
output.push_str(" = ");
273301
if let Some(value) = &entry.default {
274302
output.push_str(&inline_toml(value));
@@ -456,7 +484,7 @@ fn validate_sections(sections: &[SectionSpec]) -> Result<(), SchemaError> {
456484
for (index, section) in sections.iter().enumerate() {
457485
if sections[..index]
458486
.iter()
459-
.any(|other| other.id.eq_ignore_ascii_case(&section.id))
487+
.any(|other| keys_equal(&other.id, &section.id))
460488
{
461489
return Err(SchemaError::Invalid(format!(
462490
"重复配置 section: {}",
@@ -465,10 +493,7 @@ fn validate_sections(sections: &[SectionSpec]) -> Result<(), SchemaError> {
465493
}
466494
let mut keys = Vec::new();
467495
for entry in &section.entries {
468-
if !keys
469-
.iter()
470-
.all(|key: &String| !key.eq_ignore_ascii_case(&entry.key))
471-
{
496+
if !keys.iter().all(|key: &String| !keys_equal(key, &entry.key)) {
472497
return Err(SchemaError::Invalid(format!(
473498
"重复配置项 {}.{}",
474499
section.id, entry.key
@@ -480,7 +505,7 @@ fn validate_sections(sections: &[SectionSpec]) -> Result<(), SchemaError> {
480505
let enable = section
481506
.entries
482507
.iter()
483-
.find(|entry| entry.key.eq_ignore_ascii_case("enable"));
508+
.find(|entry| keys_equal(&entry.key, "enable"));
484509
if section.hidden || section.always_enabled {
485510
if enable.is_some() {
486511
return Err(SchemaError::Invalid(format!(
@@ -536,7 +561,7 @@ fn inject_enable_entries(
536561
let Some(enable) = section
537562
.entries
538563
.iter()
539-
.find(|entry| entry.key.eq_ignore_ascii_case("enable"))
564+
.find(|entry| keys_equal(&entry.key, "enable"))
540565
else {
541566
continue;
542567
};
@@ -555,7 +580,7 @@ fn inject_enable_entries(
555580
.as_table()
556581
.and_then(|entry| entry.get("key"))
557582
.and_then(toml::Value::as_str)
558-
.is_some_and(|key| key.eq_ignore_ascii_case("enable"))
583+
.is_some_and(|key| keys_equal(key, "enable"))
559584
}) {
560585
continue;
561586
}
@@ -580,6 +605,35 @@ fn inject_enable_entries(
580605
Ok(())
581606
}
582607

608+
fn canonicalize_document_keys(document: &mut toml::Value) -> Result<(), SchemaError> {
609+
let sections = document
610+
.as_table_mut()
611+
.and_then(|root| root.get_mut("config"))
612+
.and_then(toml::Value::as_table_mut)
613+
.and_then(|config| config.get_mut("sections"))
614+
.and_then(toml::Value::as_array_mut)
615+
.ok_or_else(|| SchemaError::Invalid("schema 缺少 config.sections".to_owned()))?;
616+
for section in sections {
617+
let Some(entries) = section
618+
.as_table_mut()
619+
.and_then(|section| section.get_mut("entries"))
620+
.and_then(toml::Value::as_array_mut)
621+
else {
622+
continue;
623+
};
624+
for entry in entries {
625+
let Some(key) = entry.as_table_mut().and_then(|entry| entry.get_mut("key")) else {
626+
continue;
627+
};
628+
let raw = key
629+
.as_str()
630+
.ok_or_else(|| SchemaError::Invalid("配置项 key 必须是字符串".to_owned()))?;
631+
*key = toml::Value::String(canonical_key(raw));
632+
}
633+
}
634+
Ok(())
635+
}
636+
583637
fn validate_entry(section: &SectionSpec, entry: &EntrySpec) -> Result<(), SchemaError> {
584638
let valid_type = matches!(
585639
entry.value_type.as_str(),
@@ -792,10 +846,7 @@ fn validate_groups(root: &toml::Table, sections: &[SectionSpec]) -> Result<(), S
792846
.get("id")
793847
.and_then(toml::Value::as_str)
794848
.ok_or_else(|| SchemaError::Invalid(format!("ui.groups[{index}] 缺少 id")))?;
795-
if ids
796-
.iter()
797-
.any(|other: &&str| other.eq_ignore_ascii_case(id))
798-
{
849+
if ids.iter().any(|other: &&str| keys_equal(other, id)) {
799850
return Err(SchemaError::Invalid(format!("重复 UI group: {id}")));
800851
}
801852
ids.push(id);
@@ -809,7 +860,7 @@ fn validate_groups(root: &toml::Table, sections: &[SectionSpec]) -> Result<(), S
809860
})?;
810861
if !sections
811862
.iter()
812-
.any(|section| section.id.eq_ignore_ascii_case(member))
863+
.any(|section| keys_equal(&section.id, member))
813864
{
814865
return Err(SchemaError::Invalid(format!(
815866
"UI group {id} 引用了未知 section: {member}"
@@ -913,14 +964,14 @@ mod tests {
913964
.expect("AM Daemon section body must exist");
914965

915966
assert!(config.starts_with("## 这是 AppleChu 的 TOML 配置文件"));
916-
assert!(config.contains("config_version = 1"));
917-
assert!(amdaemon.contains("enable = true"));
967+
assert!(config.contains("ConfigVersion = 1"));
968+
assert!(amdaemon.contains("Enable = true"));
918969
assert!(amdaemon.contains("AutoStart = false"));
919970
assert!(amdaemon.contains("AppendConfigArgs = false"));
920971
assert!(amdaemon.contains("#ConfigFiles = [\"config_*.json\"]"));
921972
assert!(document["DisableEncryption"].as_table().is_some());
922973
assert!(document["DisableTLS"].as_table().is_some());
923-
assert!(config.contains("#gameId = \"SDHD\""));
974+
assert!(config.contains("#GameId = \"SDHD\""));
924975
assert_eq!(
925976
super::SCHEMA
926977
.entry("SliderDevice", "enable")

packages/applechu/src/aime/mod.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,36 +53,28 @@ crate::config_section! {
5353
fields: {
5454
// 两种机台默认使用 COM4,同时保留分模式覆盖项
5555
pub cvt_port: u32 = 4,
56-
key: "cvtPort",
5756
comment: "CVT 模式串口号";
5857
pub sp_port: u32 = 4,
59-
key: "spPort",
6058
comment: "SP 模式串口号";
6159
pub high_baudrate: bool = true,
6260
key: "highBaud",
6361
comment: "使用 115200 高波特率(Chunithm 默认需要)";
6462
pub aime_path: String = String::from("DEVICE\\aime.txt"),
65-
key: "aimePath",
6663
comment: "Aime 卡号文件";
6764
pub felica_path: String = String::from("DEVICE\\felica.txt"),
68-
key: "felicaPath",
6965
comment: "FeliCa 卡号文件";
7066
pub authdata_path: String = String::from("DEVICE\\authdata.bin"),
71-
key: "authdataPath",
7267
comment: "认证数据文件";
7368
pub aime_gen: bool = true,
74-
key: "aimeGen",
7569
comment: "缺少 Aime 卡号时自动生成";
7670
pub felica_gen: bool = false,
77-
key: "felicaGen",
7871
comment: "缺少 FeliCa 卡号时自动生成";
7972
pub scan: i32 = 0x0D,
8073
comment: "读卡按键的虚拟键码";
8174
// 0 表示按机台模式选择:CVT=Gen2,SP=Gen3
8275
pub gen: u8 = 0,
8376
comment: "读卡器代数";
8477
pub proxy_flag: u8 = 2,
85-
key: "proxyFlag",
8678
comment: "读卡代理标志";
8779
pub iodll: String = String::new(),
8880
comment: "外部 Aime IO DLL 路径";

packages/applechu/src/amdaemon.rs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,18 @@ crate::config_section! {
111111
comment: "AM Daemon x64 winmm 劫持配置",
112112
fields: {
113113
pub auto_start: bool = false,
114-
key: "AutoStart",
115114
emit_default: true,
116115
comment: "由游戏侧启动 AM Daemon";
117116
pub executable: String = String::from("amdaemon.exe"),
118-
key: "Executable",
119117
comment: "AM Daemon 可执行文件名";
120118
pub hide_window: bool = false,
121-
key: "HideWindow",
122119
comment: "手动启动 AM Daemon 时隐藏控制台窗口";
123120
pub terminate_on_exit: bool = true,
124-
key: "TerminateOnExit",
125121
comment: "AppleChu 退出时终止 AM Daemon";
126122
pub append_config_args: bool = false,
127-
key: "AppendConfigArgs",
128123
emit_default: true,
129124
comment: "无完整 -c 参数时补充 JSON 配置";
130125
pub config_files: Vec<String> = vec![CONFIG_FILE_PATTERN.to_owned()],
131-
key: "ConfigFiles",
132126
comment: "AM Daemon JSON 配置文件列表";
133127
}
134128
}
@@ -180,16 +174,12 @@ crate::config_section! {
180174
pub title: String = String::new(),
181175
comment: "标题/其他服务器";
182176
pub replace_host: bool = false,
183-
key: "replaceHost",
184177
comment: "替换 HTTP Host";
185178
pub startup_port: u16 = 0,
186-
key: "startupPort",
187179
comment: "启动认证服务器端口";
188180
pub billing_port: u16 = 0,
189-
key: "billingPort",
190181
comment: "计费服务器端口";
191182
pub aimedb_port: u16 = 0,
192-
key: "aimedbPort",
193183
comment: "AimeDB 服务器端口";
194184
}
195185
}
@@ -208,26 +198,20 @@ crate::config_section! {
208198
key: "id",
209199
comment: "Keychip ID";
210200
pub game_id: String = String::from("SDHD"),
211-
key: "gameId",
212201
comment: "游戏 ID,默认 SDHD";
213202
pub platform_id: String = String::new(),
214-
key: "platformId",
215203
comment: "平台 ID;留空时使用当前平台默认值";
216204
pub region: u32 = 1,
217205
comment: "区域编号";
218206
pub billing_type: u32 = 1,
219-
key: "billingType",
220207
comment: "计费类型";
221208
pub system_flag: u32 = 0x64,
222-
key: "systemFlag",
223209
comment: "系统标志";
224210
pub subnet: String = String::from("192.168.139.0"),
225211
comment: "店内网络子网";
226212
pub billing_ca: String = String::from("DEVICE\\ca.crt"),
227-
key: "billingCa",
228213
comment: "计费 CA 证书";
229214
pub billing_pub: String = String::from("DEVICE\\billing.pub"),
230-
key: "billingPub",
231215
comment: "计费公钥";
232216
}
233217
}
@@ -243,13 +227,10 @@ crate::config_section! {
243227
comment: "店内网络适配器模拟",
244228
fields: {
245229
pub addr_suffix: u32 = 11,
246-
key: "addrSuffix",
247230
comment: "机台 IP 的末尾地址";
248231
pub router_suffix: u32 = 254,
249-
key: "routerSuffix",
250232
comment: "店内路由 IP 的末尾地址";
251233
pub mac_addr: String = String::from("01:02:03:04:05:06"),
252-
key: "macAddr",
253234
comment: "虚拟网卡 MAC 地址";
254235
pub broadcast: String = String::from("255.255.255.255"),
255236
comment: "UDP 广播目标地址";
@@ -282,7 +263,6 @@ crate::config_section! {
282263
comment: "AM Daemon OpenSSL 兼容",
283264
fields: {
284265
pub force_legacy_sha: bool = false,
285-
key: "forceLegacySha",
286266
comment: "强制禁用 OpenSSL SHA 扩展路径";
287267
}
288268
}

packages/applechu/src/chuniio/led_output.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,8 @@ crate::config_section! {
5151
key: "controllerLedOutputOpeNITHM",
5252
comment: "使用 OpeNITHM 控制器灯光格式";
5353
pub serial_port: String = String::from("COM5"),
54-
key: "serialPort",
5554
comment: "灯光输出串口";
5655
pub serial_baud: u32 = 921_600,
57-
key: "serialBaud",
5856
comment: "灯光输出串口波特率";
5957
}
6058
}

packages/applechu/src/config/document.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl Config {
9292
output.push('[');
9393
output.push_str(descriptor.name);
9494
output.push_str("]\n");
95-
output.push_str("enable = ");
95+
output.push_str("Enable = ");
9696
output.push_str(if loaded.enabled { "true\n" } else { "false\n" });
9797

9898
(descriptor.serialize_fields)(loaded, &mut output);
@@ -158,7 +158,7 @@ impl Config {
158158
&& value.is_table()
159159
&& !descriptors
160160
.iter()
161-
.any(|descriptor| descriptor.name.eq_ignore_ascii_case(name))
161+
.any(|descriptor| applechu_schema::keys_equal(descriptor.name, name))
162162
})
163163
.filter_map(|(name, value)| value.as_table().map(|table| (name.clone(), table.clone())))
164164
.collect();

0 commit comments

Comments
 (0)