spotify_cli/cache/
devices.rs1use std::fs;
2use std::path::PathBuf;
3
4use serde::{Deserialize, Serialize};
5
6use crate::domain::device::Device;
7use crate::error::Result;
8
9#[derive(Debug, Clone)]
11pub struct DeviceCache {
12 path: PathBuf,
13}
14
15impl DeviceCache {
16 pub fn new(path: PathBuf) -> Self {
17 Self { path }
18 }
19
20 pub fn load(&self) -> Result<Option<CacheSnapshot<Device>>> {
21 if !self.path.exists() {
22 return Ok(None);
23 }
24 let contents = fs::read_to_string(&self.path)?;
25 let snapshot = serde_json::from_str(&contents)?;
26 Ok(Some(snapshot))
27 }
28
29 pub fn save(&self, snapshot: &CacheSnapshot<Device>) -> Result<()> {
30 let payload = serde_json::to_string_pretty(snapshot)?;
31 fs::write(&self.path, payload)?;
32 Ok(())
33 }
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct CacheSnapshot<T> {
39 pub updated_at: u64,
40 pub items: Vec<T>,
41}
42
43#[cfg(test)]
44mod tests {
45 use super::{CacheSnapshot, DeviceCache};
46 use crate::domain::device::Device;
47 use std::fs;
48 use std::path::PathBuf;
49
50 fn temp_path(name: &str) -> PathBuf {
51 let mut path = std::env::temp_dir();
52 let stamp = std::time::SystemTime::now()
53 .duration_since(std::time::UNIX_EPOCH)
54 .unwrap()
55 .as_nanos();
56 path.push(format!("spotify-cli-{name}-{stamp}.json"));
57 path
58 }
59
60 #[test]
61 fn device_cache_round_trip() {
62 let path = temp_path("devices");
63 let cache = DeviceCache::new(path.clone());
64 let snapshot = CacheSnapshot {
65 updated_at: 42,
66 items: vec![Device {
67 id: "1".to_string(),
68 name: "Office".to_string(),
69 volume_percent: Some(50),
70 }],
71 };
72 cache.save(&snapshot).expect("save");
73 let loaded = cache.load().expect("load").expect("snapshot");
74 assert_eq!(loaded.updated_at, 42);
75 assert_eq!(loaded.items.len(), 1);
76 assert_eq!(loaded.items[0].name, "Office");
77 let _ = fs::remove_file(path);
78 }
79}