lisette_semantics/cache/
prelude.rs1use rustc_hash::FxHashMap as HashMap;
2use std::fs;
3use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6
7use super::types::CachedDefinition;
8use super::{COMPILER_VERSION_HASH, PRELUDE_HASH};
9use crate::prelude::{PRELUDE_FILE_ID, PRELUDE_MODULE_ID};
10use crate::store::Store;
11
12#[derive(Serialize, Deserialize)]
13pub struct PreludeCache {
14 pub content_hash: u64,
15 pub compiler_version: u64,
16 pub definitions: HashMap<String, CachedDefinition>,
17}
18
19fn cache_file_name() -> &'static str {
20 "prelude_defs.bin"
21}
22
23fn cache_path() -> Option<PathBuf> {
24 let home = std::env::var("HOME").ok()?;
25 Some(
26 PathBuf::from(home)
27 .join(".lisette")
28 .join("cache")
29 .join(cache_file_name()),
30 )
31}
32
33pub fn try_load_prelude_cache() -> Option<PreludeCache> {
34 let path = cache_path()?;
35 let bytes = fs::read(&path).ok()?;
36 let cache: PreludeCache = match bincode::deserialize(&bytes) {
37 Ok(cache) => cache,
38 Err(_) => {
39 let _ = fs::remove_file(&path);
40 return None;
41 }
42 };
43
44 if cache.content_hash != PRELUDE_HASH || cache.compiler_version != COMPILER_VERSION_HASH {
45 let _ = fs::remove_file(&path);
46 return None;
47 }
48
49 Some(cache)
50}
51
52pub fn save_prelude_cache(store: &Store) {
53 let Some(path) = cache_path() else { return };
54
55 let Some(module) = store.get_module(PRELUDE_MODULE_ID) else {
56 return;
57 };
58
59 let empty_file_map = HashMap::default();
60 let definitions: HashMap<String, CachedDefinition> = module
61 .definitions
62 .iter()
63 .map(|(name, definition)| {
64 (
65 name.to_string(),
66 CachedDefinition::from_definition(definition, &empty_file_map),
67 )
68 })
69 .collect();
70
71 let cache = PreludeCache {
72 content_hash: PRELUDE_HASH,
73 compiler_version: COMPILER_VERSION_HASH,
74 definitions,
75 };
76
77 let Ok(bytes) = bincode::serialize(&cache) else {
78 return;
79 };
80
81 let Some(parent) = path.parent() else {
82 return;
83 };
84 let _ = fs::create_dir_all(parent);
85
86 let temp_path = super::global_cache_temp_path(&path);
87 if fs::write(&temp_path, &bytes).is_err() {
88 return;
89 }
90 if fs::rename(&temp_path, &path).is_err() {
91 let _ = fs::remove_file(&temp_path);
92 return;
93 }
94 super::prune_legacy_global_caches(parent, "prelude_defs");
95}
96
97pub fn register_cached_prelude(store: &mut Store, cached: PreludeCache) {
98 store.mark_visited(PRELUDE_MODULE_ID);
99
100 use syntax::program::File;
103 store.store_file(
104 PRELUDE_MODULE_ID,
105 File {
106 id: PRELUDE_FILE_ID,
107 module_id: PRELUDE_MODULE_ID.to_string(),
108 name: "prelude.d.lis".to_string(),
109 display_path: "prelude.d.lis".to_string(),
110 source: stdlib::LIS_PRELUDE_SOURCE.to_string(),
111 items: vec![],
112 },
113 );
114
115 let file_ids: &[u32] = &[];
116 let module = store
117 .get_module_mut(PRELUDE_MODULE_ID)
118 .expect("prelude module must be registered before loading cached definitions");
119 for (qualified_name, cached_definition) in cached.definitions {
120 let definition = cached_definition.to_definition(file_ids);
121 module.definitions.insert(qualified_name.into(), definition);
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn cache_file_name_is_stable() {
131 assert_eq!(cache_file_name(), "prelude_defs.bin");
132 }
133}