1use std::fs;
2use std::path::Path;
3
4use super::{OfficialIndex, idx};
5
6pub fn load_from_disk(path: &Path) {
18 if let Ok(s) = fs::read_to_string(path)
19 && let Ok(mut new_idx) = serde_json::from_str::<OfficialIndex>(&s)
20 && let Ok(mut guard) = idx().write()
21 {
22 new_idx.rebuild_name_index();
24 *guard = new_idx;
25 }
26}
27
28pub fn save_to_disk(path: &Path) {
42 if let Ok(guard) = idx().read()
43 && let Ok(s) = serde_json::to_string(&*guard)
44 {
45 if guard.pkgs.is_empty() {
47 tracing::warn!(
48 path = %path.display(),
49 "Attempting to save empty index to disk"
50 );
51 }
52 if let Some(parent) = path.parent()
54 && let Err(e) = fs::create_dir_all(parent)
55 {
56 tracing::warn!(
57 path = %path.display(),
58 error = %e,
59 "Failed to create parent directory for index file"
60 );
61 return;
62 }
63 if let Err(e) = fs::write(path, s) {
65 tracing::warn!(
66 path = %path.display(),
67 error = %e,
68 "Failed to write index file to disk"
69 );
70 } else {
71 tracing::info!(
72 path = %path.display(),
73 package_count = guard.pkgs.len(),
74 "Successfully saved index to disk"
75 );
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82
83 #[tokio::test]
84 async fn index_loads_deduped_and_sorted_after_multiple_writes() {
95 use std::path::PathBuf;
96
97 let mut path: PathBuf = std::env::temp_dir();
98 path.push(format!(
99 "pacsea_idx_multi_{}_{}.json",
100 std::process::id(),
101 std::time::SystemTime::now()
102 .duration_since(std::time::UNIX_EPOCH)
103 .expect("System time is before UNIX epoch")
104 .as_nanos()
105 ));
106
107 let idx_json1 = serde_json::json!({
108 "pkgs": [
109 {"name": "zz", "repo": "extra", "arch": "x86_64", "version": "1", "description": ""},
110 {"name": "aa", "repo": "core", "arch": "x86_64", "version": "1", "description": ""}
111 ]
112 });
113 std::fs::write(
114 &path,
115 serde_json::to_string(&idx_json1).expect("failed to serialize index JSON"),
116 )
117 .expect("failed to write index JSON file");
118 super::load_from_disk(&path);
119
120 let idx_json2 = serde_json::json!({
121 "pkgs": [
122 {"name": "aa", "repo": "core", "arch": "x86_64", "version": "2", "description": ""},
123 {"name": "zz", "repo": "extra", "arch": "x86_64", "version": "1", "description": ""}
124 ]
125 });
126 std::fs::write(
127 &path,
128 serde_json::to_string(&idx_json2).expect("failed to serialize index JSON"),
129 )
130 .expect("failed to write index JSON file");
131 super::load_from_disk(&path);
132
133 let all = crate::index::all_official();
134 let mut names: Vec<String> = all.into_iter().map(|p| p.name).collect();
135 names.sort();
136 names.dedup();
137 assert_eq!(names, vec!["aa", "zz"]);
138
139 let _ = std::fs::remove_file(&path);
140 }
141
142 #[tokio::test]
143 async fn index_save_writes_current_state_to_disk() {
154 use std::path::PathBuf;
155 if let Ok(mut g) = super::idx().write() {
157 g.pkgs = vec![crate::index::OfficialPkg {
158 name: "abc".to_string(),
159 repo: "core".to_string(),
160 arch: "x86_64".to_string(),
161 version: "9".to_string(),
162 description: "desc".to_string(),
163 }];
164 }
165 let mut path: PathBuf = std::env::temp_dir();
167 path.push(format!(
168 "pacsea_idx_save_{}_{}.json",
169 std::process::id(),
170 std::time::SystemTime::now()
171 .duration_since(std::time::UNIX_EPOCH)
172 .expect("System time is before UNIX epoch")
173 .as_nanos()
174 ));
175 super::save_to_disk(&path);
176 let body = std::fs::read_to_string(&path).expect("failed to read index JSON file");
178 assert!(body.contains("\"abc\""));
179 let _ = std::fs::remove_file(&path);
180 }
181}