@@ -570,7 +570,7 @@ fn migrate_settings_schema(app: &NodeupApp) -> Result<SchemaMigrationResult> {
570570
571571 let content = fs:: read_to_string ( & file_path) ?;
572572 let raw_value: Value = toml:: from_str ( & content) ?;
573- let from_schema = extract_schema_version ( & raw_value) ?;
573+ let from_schema = extract_schema_version ( & raw_value, & file_path ) ?;
574574
575575 if from_schema > SETTINGS_SCHEMA_VERSION {
576576 return Err ( self_invalid_input ( format ! (
@@ -588,7 +588,7 @@ fn migrate_settings_schema(app: &NodeupApp) -> Result<SchemaMigrationResult> {
588588 } ) ;
589589 }
590590
591- let migrated = migrate_settings_legacy ( & raw_value, from_schema) ?;
591+ let migrated = migrate_settings_legacy ( & raw_value, from_schema, & file_path ) ?;
592592 app. store . save_settings ( & migrated) ?;
593593
594594 Ok ( SchemaMigrationResult {
@@ -614,7 +614,7 @@ fn migrate_overrides_schema(app: &NodeupApp) -> Result<SchemaMigrationResult> {
614614
615615 let content = fs:: read_to_string ( & file_path) ?;
616616 let raw_value: Value = toml:: from_str ( & content) ?;
617- let from_schema = extract_schema_version ( & raw_value) ?;
617+ let from_schema = extract_schema_version ( & raw_value, & file_path ) ?;
618618
619619 if from_schema > OVERRIDES_SCHEMA_VERSION {
620620 return Err ( self_invalid_input ( format ! (
@@ -632,7 +632,7 @@ fn migrate_overrides_schema(app: &NodeupApp) -> Result<SchemaMigrationResult> {
632632 } ) ;
633633 }
634634
635- let migrated = migrate_overrides_legacy ( & raw_value, from_schema) ?;
635+ let migrated = migrate_overrides_legacy ( & raw_value, from_schema, & file_path ) ?;
636636 app. overrides . save ( & migrated) ?;
637637
638638 Ok ( SchemaMigrationResult {
@@ -643,40 +643,60 @@ fn migrate_overrides_schema(app: &NodeupApp) -> Result<SchemaMigrationResult> {
643643 } )
644644}
645645
646- fn extract_schema_version ( value : & Value ) -> Result < u32 > {
647- let table = value
648- . as_table ( )
649- . ok_or_else ( || self_invalid_input ( "Expected a TOML table at the document root" ) ) ?;
646+ fn extract_schema_version ( value : & Value , file_path : & Path ) -> Result < u32 > {
647+ let table = value. as_table ( ) . ok_or_else ( || {
648+ self_invalid_input ( format ! (
649+ "Expected TOML table at document root (file={}, actual_type={})" ,
650+ file_path. display( ) ,
651+ toml_value_type( value)
652+ ) )
653+ } ) ?;
650654
651655 let Some ( version_value) = table. get ( "schema_version" ) else {
652656 return Ok ( 0 ) ;
653657 } ;
654658
655- let version = version_value
656- . as_integer ( )
657- . ok_or_else ( || self_invalid_input ( "schema_version must be an integer" ) ) ?;
659+ let version = version_value. as_integer ( ) . ok_or_else ( || {
660+ self_invalid_input ( format ! (
661+ "schema_version must be an integer (file={}, actual_type={})" ,
662+ file_path. display( ) ,
663+ toml_value_type( version_value)
664+ ) )
665+ } ) ?;
658666
659667 if version < 0 {
660- return Err ( self_invalid_input ( "schema_version cannot be negative" ) ) ;
668+ return Err ( self_invalid_input ( format ! (
669+ "schema_version cannot be negative (file={}, value={version})" ,
670+ file_path. display( )
671+ ) ) ) ;
661672 }
662673
663674 Ok ( version as u32 )
664675}
665676
666- fn migrate_settings_legacy ( value : & Value , from_schema : u32 ) -> Result < SettingsFile > {
677+ fn migrate_settings_legacy (
678+ value : & Value ,
679+ from_schema : u32 ,
680+ file_path : & Path ,
681+ ) -> Result < SettingsFile > {
667682 if from_schema != 0 {
668683 return Err ( self_invalid_input ( format ! (
669- "Unsupported legacy settings schema version: {from_schema}"
684+ "Unsupported legacy settings schema version: {from_schema} (file={})" ,
685+ file_path. display( )
670686 ) ) ) ;
671687 }
672688
673- let table = value
674- . as_table ( )
675- . ok_or_else ( || self_invalid_input ( "Expected settings file to be a TOML table" ) ) ?;
689+ let table = value. as_table ( ) . ok_or_else ( || {
690+ self_invalid_input ( format ! (
691+ "Expected settings file to be a TOML table (file={}, actual_type={})" ,
692+ file_path. display( ) ,
693+ toml_value_type( value)
694+ ) )
695+ } ) ?;
676696
677- let default_selector = optional_string ( table, "default_selector" ) ?;
678- let linked_runtimes = string_table ( table, "linked_runtimes" ) ?;
679- let tracked_selectors = string_array ( table, "tracked_selectors" ) ?;
697+ let default_selector = optional_string ( table, "default_selector" , file_path ) ?;
698+ let linked_runtimes = string_table ( table, "linked_runtimes" , file_path ) ?;
699+ let tracked_selectors = string_array ( table, "tracked_selectors" , file_path ) ?;
680700
681701 Ok ( SettingsFile {
682702 schema_version : SETTINGS_SCHEMA_VERSION ,
@@ -686,19 +706,28 @@ fn migrate_settings_legacy(value: &Value, from_schema: u32) -> Result<SettingsFi
686706 } )
687707}
688708
689- fn migrate_overrides_legacy ( value : & Value , from_schema : u32 ) -> Result < OverridesFile > {
709+ fn migrate_overrides_legacy (
710+ value : & Value ,
711+ from_schema : u32 ,
712+ file_path : & Path ,
713+ ) -> Result < OverridesFile > {
690714 if from_schema != 0 {
691715 return Err ( self_invalid_input ( format ! (
692- "Unsupported legacy overrides schema version: {from_schema}"
716+ "Unsupported legacy overrides schema version: {from_schema} (file={})" ,
717+ file_path. display( )
693718 ) ) ) ;
694719 }
695720
696- let table = value
697- . as_table ( )
698- . ok_or_else ( || self_invalid_input ( "Expected overrides file to be a TOML table" ) ) ?;
721+ let table = value. as_table ( ) . ok_or_else ( || {
722+ self_invalid_input ( format ! (
723+ "Expected overrides file to be a TOML table (file={}, actual_type={})" ,
724+ file_path. display( ) ,
725+ toml_value_type( value)
726+ ) )
727+ } ) ?;
699728
700729 let entries = if let Some ( entries_value) = table. get ( "entries" ) {
701- parse_override_entries ( entries_value) ?
730+ parse_override_entries ( entries_value, file_path ) ?
702731 } else {
703732 Vec :: new ( )
704733 } ;
@@ -709,79 +738,115 @@ fn migrate_overrides_legacy(value: &Value, from_schema: u32) -> Result<Overrides
709738 } )
710739}
711740
712- fn optional_string ( table : & Table , field : & str ) -> Result < Option < String > > {
741+ fn optional_string ( table : & Table , field : & str , file_path : & Path ) -> Result < Option < String > > {
713742 let Some ( value) = table. get ( field) else {
714743 return Ok ( None ) ;
715744 } ;
716745
717- let string = value
718- . as_str ( )
719- . ok_or_else ( || self_invalid_input ( format ! ( "Expected '{field}' to be a string" ) ) ) ?;
746+ let string = value. as_str ( ) . ok_or_else ( || {
747+ self_invalid_input ( format ! (
748+ "Expected '{field}' to be a string (file={}, actual_type={})" ,
749+ file_path. display( ) ,
750+ toml_value_type( value)
751+ ) )
752+ } ) ?;
720753
721754 Ok ( Some ( string. to_string ( ) ) )
722755}
723756
724- fn string_table ( table : & Table , field : & str ) -> Result < BTreeMap < String , String > > {
757+ fn string_table ( table : & Table , field : & str , file_path : & Path ) -> Result < BTreeMap < String , String > > {
725758 let Some ( value) = table. get ( field) else {
726759 return Ok ( BTreeMap :: new ( ) ) ;
727760 } ;
728761
729- let map = value
730- . as_table ( )
731- . ok_or_else ( || self_invalid_input ( format ! ( "Expected '{field}' to be a table" ) ) ) ?;
762+ let map = value. as_table ( ) . ok_or_else ( || {
763+ self_invalid_input ( format ! (
764+ "Expected '{field}' to be a table (file={}, actual_type={})" ,
765+ file_path. display( ) ,
766+ toml_value_type( value)
767+ ) )
768+ } ) ?;
732769
733770 let mut result = BTreeMap :: new ( ) ;
734771 for ( key, item) in map {
735772 let value = item. as_str ( ) . ok_or_else ( || {
736- self_invalid_input ( format ! ( "Expected '{field}.{key}' to be a string" ) )
773+ self_invalid_input ( format ! (
774+ "Expected '{field}.{key}' to be a string (file={}, actual_type={})" ,
775+ file_path. display( ) ,
776+ toml_value_type( item)
777+ ) )
737778 } ) ?;
738779 result. insert ( key. clone ( ) , value. to_string ( ) ) ;
739780 }
740781
741782 Ok ( result)
742783}
743784
744- fn string_array ( table : & Table , field : & str ) -> Result < Vec < String > > {
785+ fn string_array ( table : & Table , field : & str , file_path : & Path ) -> Result < Vec < String > > {
745786 let Some ( value) = table. get ( field) else {
746787 return Ok ( Vec :: new ( ) ) ;
747788 } ;
748789
749- let items = value
750- . as_array ( )
751- . ok_or_else ( || self_invalid_input ( format ! ( "Expected '{field}' to be an array" ) ) ) ?;
790+ let items = value. as_array ( ) . ok_or_else ( || {
791+ self_invalid_input ( format ! (
792+ "Expected '{field}' to be an array (file={}, actual_type={})" ,
793+ file_path. display( ) ,
794+ toml_value_type( value)
795+ ) )
796+ } ) ?;
752797
753798 let mut result = Vec :: new ( ) ;
754799 for ( index, item) in items. iter ( ) . enumerate ( ) {
755800 let value = item. as_str ( ) . ok_or_else ( || {
756- self_invalid_input ( format ! ( "Expected '{field}[{index}]' to be a string" ) )
801+ self_invalid_input ( format ! (
802+ "Expected '{field}[{index}]' to be a string (file={}, actual_type={})" ,
803+ file_path. display( ) ,
804+ toml_value_type( item)
805+ ) )
757806 } ) ?;
758807 result. push ( value. to_string ( ) ) ;
759808 }
760809
761810 Ok ( result)
762811}
763812
764- fn parse_override_entries ( value : & Value ) -> Result < Vec < OverrideEntry > > {
765- let items = value
766- . as_array ( )
767- . ok_or_else ( || self_invalid_input ( "Expected 'entries' to be an array" ) ) ?;
813+ fn parse_override_entries ( value : & Value , file_path : & Path ) -> Result < Vec < OverrideEntry > > {
814+ let items = value. as_array ( ) . ok_or_else ( || {
815+ self_invalid_input ( format ! (
816+ "Expected 'entries' to be an array (file={}, actual_type={})" ,
817+ file_path. display( ) ,
818+ toml_value_type( value)
819+ ) )
820+ } ) ?;
768821
769822 let mut entries = Vec :: new ( ) ;
770823 for ( index, item) in items. iter ( ) . enumerate ( ) {
771824 let table = item. as_table ( ) . ok_or_else ( || {
772- self_invalid_input ( format ! ( "Expected 'entries[{index}]' to be a table" ) )
825+ self_invalid_input ( format ! (
826+ "Expected 'entries[{index}]' to be a table (file={}, actual_type={})" ,
827+ file_path. display( ) ,
828+ toml_value_type( item)
829+ ) )
773830 } ) ?;
774831
775832 let path = table. get ( "path" ) . and_then ( Value :: as_str) . ok_or_else ( || {
776- self_invalid_input ( format ! ( "Expected 'entries[{index}].path' to be a string" ) )
833+ let actual_type = table. get ( "path" ) . map ( toml_value_type) . unwrap_or ( "none" ) ;
834+ self_invalid_input ( format ! (
835+ "Expected 'entries[{index}].path' to be a string (file={}, \
836+ actual_type={actual_type})",
837+ file_path. display( )
838+ ) )
777839 } ) ?;
778840
779841 let selector = table
780842 . get ( "selector" )
781843 . and_then ( Value :: as_str)
782844 . ok_or_else ( || {
845+ let actual_type = table. get ( "selector" ) . map ( toml_value_type) . unwrap_or ( "none" ) ;
783846 self_invalid_input ( format ! (
784- "Expected 'entries[{index}].selector' to be a string"
847+ "Expected 'entries[{index}].selector' to be a string (file={}, \
848+ actual_type={actual_type})",
849+ file_path. display( )
785850 ) )
786851 } ) ?;
787852
@@ -794,6 +859,18 @@ fn parse_override_entries(value: &Value) -> Result<Vec<OverrideEntry>> {
794859 Ok ( entries)
795860}
796861
862+ fn toml_value_type ( value : & Value ) -> & ' static str {
863+ match value {
864+ Value :: String ( _) => "string" ,
865+ Value :: Integer ( _) => "integer" ,
866+ Value :: Float ( _) => "float" ,
867+ Value :: Boolean ( _) => "boolean" ,
868+ Value :: Datetime ( _) => "datetime" ,
869+ Value :: Array ( _) => "array" ,
870+ Value :: Table ( _) => "table" ,
871+ }
872+ }
873+
797874fn log_failure ( action : SelfAction , error : NodeupError ) -> NodeupError {
798875 info ! (
799876 command_path = action. command_path( ) ,
0 commit comments