pacsea/app/
services_cache.rs1use super::cache_common;
4use crate::state::modal::ServiceImpact;
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct ServiceCache {
15 pub install_list_signature: Vec<String>,
17 pub services: Vec<ServiceImpact>,
19}
20
21#[must_use]
33pub fn compute_signature(packages: &[crate::state::PackageItem]) -> Vec<String> {
34 cache_common::compute_signature(packages)
35}
36
37#[allow(clippy::ptr_arg)]
53#[must_use]
54pub fn load_cache(path: &PathBuf, current_signature: &[String]) -> Option<Vec<ServiceImpact>> {
55 let ServiceCache {
56 install_list_signature,
57 services,
58 } = cache_common::load_signed_cache(path, "Service", "service")?;
59 cache_common::take_exact_match(
60 path,
61 "service",
62 current_signature,
63 &install_list_signature,
64 services,
65 )
66}
67
68#[allow(clippy::ptr_arg)]
83pub fn save_cache(path: &PathBuf, signature: &[String], services: &[ServiceImpact]) {
84 let cache = ServiceCache {
85 install_list_signature: signature.to_vec(),
86 services: services.to_vec(),
87 };
88 cache_common::save_signed_cache(path, &cache, services.len(), "service");
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use crate::state::modal::{ServiceImpact, ServiceRestartDecision};
95 use crate::state::{PackageItem, Source};
96 use std::fs;
97 use std::time::{SystemTime, UNIX_EPOCH};
98
99 fn temp_path(label: &str) -> std::path::PathBuf {
100 let mut path = std::env::temp_dir();
101 path.push(format!(
102 "pacsea_services_cache_{label}_{}_{}.json",
103 std::process::id(),
104 SystemTime::now()
105 .duration_since(UNIX_EPOCH)
106 .expect("System time is before UNIX epoch")
107 .as_nanos()
108 ));
109 path
110 }
111
112 fn sample_packages() -> Vec<PackageItem> {
113 vec![
114 PackageItem {
115 name: "sshd".into(),
116 version: "9.0.0".into(),
117 description: String::new(),
118 source: Source::Official {
119 repo: "core".into(),
120 arch: "x86_64".into(),
121 },
122 popularity: None,
123 out_of_date: None,
124 orphaned: false,
125 },
126 PackageItem {
127 name: "nginx".into(),
128 version: "1.24.0".into(),
129 description: String::new(),
130 source: Source::Official {
131 repo: "extra".into(),
132 arch: "x86_64".into(),
133 },
134 popularity: None,
135 out_of_date: None,
136 orphaned: false,
137 },
138 ]
139 }
140
141 fn sample_services() -> Vec<ServiceImpact> {
142 vec![ServiceImpact {
143 unit_name: "sshd.service".into(),
144 providers: vec!["sshd".into()],
145 is_active: true,
146 needs_restart: true,
147 recommended_decision: ServiceRestartDecision::Restart,
148 restart_decision: ServiceRestartDecision::Restart,
149 }]
150 }
151
152 #[test]
153 fn compute_signature_orders_package_names() {
160 let mut packages = sample_packages();
161 packages.reverse();
162 let signature = compute_signature(&packages);
163 assert_eq!(signature, vec![String::from("nginx"), String::from("sshd")]);
164 }
165
166 #[test]
167 fn load_cache_rejects_signature_mismatch() {
174 let path = temp_path("mismatch");
175 let packages = sample_packages();
176 let signature = compute_signature(&packages);
177 let services = sample_services();
178 save_cache(&path, &signature, &services);
179
180 let mismatched_signature = vec!["sshd".into(), "httpd".into()];
181 assert!(load_cache(&path, &mismatched_signature).is_none());
182 let _ = fs::remove_file(&path);
183 }
184
185 #[test]
186 fn save_and_load_cache_roundtrip() {
193 let path = temp_path("roundtrip");
194 let packages = sample_packages();
195 let signature = compute_signature(&packages);
196 let services = sample_services();
197 save_cache(&path, &signature, &services);
198
199 let reloaded = load_cache(&path, &signature).expect("expected cache to load");
200 assert_eq!(reloaded.len(), services.len());
201 assert_eq!(reloaded[0].unit_name, services[0].unit_name);
202 assert_eq!(reloaded[0].providers, services[0].providers);
203 assert_eq!(reloaded[0].is_active, services[0].is_active);
204 assert_eq!(reloaded[0].needs_restart, services[0].needs_restart);
205
206 let _ = fs::remove_file(&path);
207 }
208}