lisette_semantics/cache/
go_stdlib.rs1use rustc_hash::FxHashMap as HashMap;
2use std::fs;
3use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6use stdlib::get_go_stdlib_typedef;
7
8use super::types::CachedDefinition;
9use super::{COMPILER_VERSION_HASH, GO_STDLIB_HASH};
10use crate::store::Store;
11
12#[derive(Serialize, Deserialize)]
13pub struct GoStdlibCache {
14 pub content_hash: u64,
15 pub compiler_version: u64,
16 pub modules: HashMap<String, GoModuleCache>,
17}
18
19#[derive(Serialize, Deserialize)]
20pub struct GoModuleCache {
21 pub definitions: HashMap<String, CachedDefinition>,
22 pub go_imports: Vec<String>,
24}
25
26fn cache_path() -> Option<PathBuf> {
27 let home = std::env::var("HOME").ok()?;
28 Some(
29 PathBuf::from(home)
30 .join(".lisette")
31 .join("cache")
32 .join(format!(
33 "stdlib_defs_{:x}_compiler_{:x}.bin",
34 GO_STDLIB_HASH & 0xFFFFFF,
35 COMPILER_VERSION_HASH & 0xFFFFFF
36 )),
37 )
38}
39
40pub fn try_load_go_stdlib_cache() -> Option<GoStdlibCache> {
41 let path = cache_path()?;
42 let bytes = fs::read(&path).ok()?;
43 let cache: GoStdlibCache = bincode::deserialize(&bytes).ok()?;
44
45 if cache.content_hash != GO_STDLIB_HASH || cache.compiler_version != COMPILER_VERSION_HASH {
46 let _ = fs::remove_file(&path);
47 return None;
48 }
49
50 Some(cache)
51}
52
53pub fn save_go_stdlib_cache(store: &Store, go_module_ids: &[String]) {
54 let Some(path) = cache_path() else { return };
55
56 let mut modules = HashMap::default();
57 let empty_file_map = HashMap::default();
59 for module_id in go_module_ids {
60 let Some(module) = store.get_module(module_id) else {
61 continue;
62 };
63 let definitions: HashMap<String, CachedDefinition> = module
64 .definitions
65 .iter()
66 .map(|(name, definition)| {
67 (
68 name.to_string(),
69 CachedDefinition::from_definition(definition, &empty_file_map),
70 )
71 })
72 .collect();
73
74 let go_imports = get_go_imports_from_source(module_id);
75
76 modules.insert(
77 module_id.clone(),
78 GoModuleCache {
79 definitions,
80 go_imports,
81 },
82 );
83 }
84
85 let cache = GoStdlibCache {
86 content_hash: GO_STDLIB_HASH,
87 compiler_version: COMPILER_VERSION_HASH,
88 modules,
89 };
90
91 let Ok(bytes) = bincode::serialize(&cache) else {
92 return;
93 };
94
95 if let Some(parent) = path.parent() {
96 let _ = fs::create_dir_all(parent);
97 }
98
99 let temp_path = path.with_extension("bin.tmp");
100 if fs::write(&temp_path, bytes).is_ok() {
101 let _ = fs::rename(&temp_path, &path);
102 }
103}
104
105pub fn load_cached_go_module(store: &mut Store, module_id: &str, cache: &GoStdlibCache) {
107 if store.is_visited(module_id) {
108 return;
109 }
110
111 let Some(cached) = cache.modules.get(module_id) else {
112 return;
113 };
114
115 let imports = cached.go_imports.clone();
117 for dep in &imports {
118 load_cached_go_module(store, dep, cache);
119 }
120
121 if store.is_visited(module_id) {
122 return; }
124
125 register_cached_go_module(store, module_id, cached);
126}
127
128fn register_cached_go_module(store: &mut Store, module_id: &str, cached: &GoModuleCache) {
129 store.add_module(module_id);
130 store.mark_visited(module_id);
131
132 let file_ids: &[u32] = &[];
136
137 let module = store.get_module_mut(module_id).unwrap();
138 for (qualified_name, cached_definition) in &cached.definitions {
139 let definition = cached_definition.to_definition(file_ids);
140 module
141 .definitions
142 .insert(qualified_name.clone().into(), definition);
143 }
144}
145
146fn get_go_imports_from_source(module_id: &str) -> Vec<String> {
148 let Some(go_pkg) = module_id.strip_prefix("go:") else {
149 return vec![];
150 };
151 let Some(source) = get_go_stdlib_typedef(go_pkg) else {
152 return vec![];
153 };
154 source
155 .lines()
156 .filter_map(|line| {
157 let line = line.trim();
158 let rest = line.strip_prefix("import \"go:")?;
159 let pkg = rest.strip_suffix('"')?;
160 Some(format!("go:{pkg}"))
161 })
162 .collect()
163}