@@ -75,17 +75,27 @@ impl<'a, 'py> FromPyObject<'a, 'py> for ImportsByModule {
7575 }
7676}
7777
78+ /// The version of the data cache file format.
79+ ///
80+ /// Bump this whenever the serialized structure changes. On read, a file with a
81+ /// different (or missing) version is treated as a version mismatch and rebuilt,
82+ /// rather than being mistaken for a corrupt file.
83+ const CACHE_SCHEMA_VERSION : u32 = 2 ;
84+
7885fn serialize_imports_by_module (
7986 imports_by_module : & HashMap < Module , HashSet < DirectImport > > ,
8087) -> String {
81- let raw_map: HashMap < & str , Vec < ( & str , usize , & str ) > > = imports_by_module
88+ // Fields are ordered to match the `get_import_details` dict (`imported`, `is_lazy`,
89+ // `line_number`, `line_contents`); `importer` is the map key.
90+ let raw_map: HashMap < & str , Vec < ( & str , bool , usize , & str ) > > = imports_by_module
8291 . iter ( )
8392 . map ( |( module, imports) | {
84- let imports_vec: Vec < ( & str , usize , & str ) > = imports
93+ let imports_vec: Vec < ( & str , bool , usize , & str ) > = imports
8594 . iter ( )
8695 . map ( |import| {
8796 (
8897 import. imported . as_str ( ) ,
98+ import. is_lazy ,
8999 import. line_number ,
90100 import. line_contents . as_str ( ) ,
91101 )
@@ -95,16 +105,33 @@ fn serialize_imports_by_module(
95105 } )
96106 . collect ( ) ;
97107
98- serde_json:: to_string ( & raw_map) . expect ( "Failed to serialize to JSON" )
108+ let envelope = serde_json:: json!( {
109+ "version" : CACHE_SCHEMA_VERSION ,
110+ "imports_by_module" : raw_map,
111+ } ) ;
112+
113+ serde_json:: to_string ( & envelope) . expect ( "Failed to serialize to JSON" )
99114}
100115
101116pub fn parse_json_to_map (
102117 json_str : & str ,
103118 filename : & str ,
104119) -> GrimpResult < HashMap < Module , HashSet < DirectImport > > > {
105- let raw_map: HashMap < String , Vec < ( String , usize , String ) > > = serde_json:: from_str ( json_str)
120+ // Parse into a generic value first, so we can distinguish genuinely corrupt
121+ // JSON from a cache file written by a different format version (e.g. by an
122+ // older Grimp). The latter should be silently rebuilt, not warned about.
123+ let value: serde_json:: Value = serde_json:: from_str ( json_str)
106124 . map_err ( |_| GrimpError :: CorruptCache ( filename. to_string ( ) ) ) ?;
107125
126+ let version = value. get ( "version" ) . and_then ( |v| v. as_u64 ( ) ) ;
127+ if version != Some ( CACHE_SCHEMA_VERSION as u64 ) {
128+ return Err ( GrimpError :: CacheVersionMismatch ( filename. to_string ( ) ) ) ;
129+ }
130+
131+ let raw_map: HashMap < String , Vec < ( String , bool , usize , String ) > > =
132+ serde_json:: from_value ( value. get ( "imports_by_module" ) . cloned ( ) . unwrap_or_default ( ) )
133+ . map_err ( |_| GrimpError :: CorruptCache ( filename. to_string ( ) ) ) ?;
134+
108135 let mut parsed_map: HashMap < Module , HashSet < DirectImport > > = HashMap :: new ( ) ;
109136
110137 for ( module_name, imports) in raw_map {
@@ -113,13 +140,15 @@ pub fn parse_json_to_map(
113140 } ;
114141 let import_set: HashSet < DirectImport > = imports
115142 . into_iter ( )
116- . map ( |( imported, line_number, line_contents) | DirectImport {
117- importer : module_name. clone ( ) ,
118- imported,
119- line_number,
120- line_contents,
121- is_lazy : false , // TODO get working with cache.
122- } )
143+ . map (
144+ |( imported, is_lazy, line_number, line_contents) | DirectImport {
145+ importer : module_name. clone ( ) ,
146+ imported,
147+ line_number,
148+ line_contents,
149+ is_lazy,
150+ } ,
151+ )
123152 . collect ( ) ;
124153 parsed_map. insert ( module, import_set) ;
125154 }
0 commit comments