@@ -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 ) ]
2147pub 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+
583637fn 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" )
0 commit comments