@@ -308,6 +308,10 @@ pub struct Config {
308308 #[ serde( skip) ]
309309 deprecation_warnings : Vec < DeprecationWarning > ,
310310
311+ // Holds unknown fields we encountered while parsing
312+ #[ serde( skip, default ) ]
313+ unknown_fields : Vec < String > ,
314+
311315 #[ serde( default = "default_path" ) ]
312316 pub path : PathBuf ,
313317}
@@ -435,9 +439,14 @@ impl Config {
435439
436440 /// Try to convert a bsconfig from a string to a bsconfig struct
437441 pub fn new_from_json_string ( config_str : & str ) -> Result < Self > {
438- let mut config = serde_json:: from_str :: < Config > ( config_str) ?;
442+ let mut deserializer = serde_json:: Deserializer :: from_str ( config_str) ;
443+ let mut unknown_fields = Vec :: new ( ) ;
444+ let mut config: Config = serde_ignored:: deserialize ( & mut deserializer, |path| {
445+ unknown_fields. push ( path. to_string ( ) ) ;
446+ } ) ?;
439447
440448 config. handle_deprecations ( ) ?;
449+ config. unknown_fields = unknown_fields;
441450
442451 Ok ( config)
443452 }
@@ -662,6 +671,42 @@ impl Config {
662671 & self . deprecation_warnings
663672 }
664673
674+ pub fn get_unknown_fields ( & self ) -> Vec < String > {
675+ self . unknown_fields
676+ . iter ( )
677+ . filter ( |field| !self . is_unsupported_field ( field) )
678+ . cloned ( )
679+ . collect ( )
680+ }
681+
682+ pub fn get_unsupported_fields ( & self ) -> Vec < String > {
683+ self . unknown_fields
684+ . iter ( )
685+ . filter ( |field| self . is_unsupported_field ( field) )
686+ . cloned ( )
687+ . collect :: < Vec < _ > > ( )
688+ }
689+
690+ fn is_unsupported_field ( & self , field : & str ) -> bool {
691+ const UNSUPPORTED_TOP_LEVEL_FIELDS : & [ & str ] = & [
692+ "version" ,
693+ "ignored-dirs" ,
694+ "generators" ,
695+ "cut-generators" ,
696+ "pp-flags" ,
697+ "js-post-build" ,
698+ "entries" ,
699+ "use-stdlib" ,
700+ "external-stdlib" ,
701+ "bs-external-includes" ,
702+ "reanalyze" ,
703+ ] ;
704+
705+ let top_level = field. split ( |c| [ '.' , '[' ] . contains ( & c) ) . next ( ) . unwrap_or ( field) ;
706+
707+ UNSUPPORTED_TOP_LEVEL_FIELDS . contains ( & top_level)
708+ }
709+
665710 fn handle_deprecations ( & mut self ) -> Result < ( ) > {
666711 if self . dependencies . is_some ( ) && self . bs_dependencies . is_some ( ) {
667712 bail ! ( "dependencies and bs-dependencies are mutually exclusive. Please use 'dependencies'." ) ;
@@ -729,6 +774,7 @@ pub mod tests {
729774 deprecation_warnings : vec ! [ ] ,
730775 experimental_features : None ,
731776 allowed_dependents : args. allowed_dependents ,
777+ unknown_fields : vec ! [ ] ,
732778 path : args. path ,
733779 }
734780 }
@@ -1023,6 +1069,42 @@ pub mod tests {
10231069 assert ! ( config. get_deprecations( ) . is_empty( ) ) ;
10241070 }
10251071
1072+ #[ test]
1073+ fn test_unknown_fields_are_collected ( ) {
1074+ let json = r#"
1075+ {
1076+ "name": "testrepo",
1077+ "sources": {
1078+ "dir": "src",
1079+ "subdirs": true
1080+ },
1081+ "some-new-field": true
1082+ }
1083+ "# ;
1084+
1085+ let config = Config :: new_from_json_string ( json) . expect ( "a valid json string" ) ;
1086+ assert_eq ! ( config. get_unknown_fields( ) , vec![ "some-new-field" . to_string( ) ] ) ;
1087+ assert ! ( config. get_unsupported_fields( ) . is_empty( ) ) ;
1088+ }
1089+
1090+ #[ test]
1091+ fn test_unsupported_fields_are_collected ( ) {
1092+ let json = r#"
1093+ {
1094+ "name": "testrepo",
1095+ "sources": {
1096+ "dir": "src",
1097+ "subdirs": true
1098+ },
1099+ "ignored-dirs": ["scripts"]
1100+ }
1101+ "# ;
1102+
1103+ let config = Config :: new_from_json_string ( json) . expect ( "a valid json string" ) ;
1104+ assert_eq ! ( config. get_unsupported_fields( ) , vec![ "ignored-dirs" . to_string( ) ] ) ;
1105+ assert ! ( config. get_unknown_fields( ) . is_empty( ) ) ;
1106+ }
1107+
10261108 #[ test]
10271109 fn test_compiler_flags ( ) {
10281110 let json = r#"
0 commit comments