spotify_cli/cache/
mod.rs

1//! Cache storage for devices, playlists, pins, search results, and metadata.
2use std::env;
3use std::fs;
4use std::path::{Path, PathBuf};
5
6use crate::cache::devices::DeviceCache;
7use crate::cache::metadata::MetadataStore;
8use crate::cache::playlists::PlaylistCache;
9use crate::error::Result;
10
11pub mod devices;
12pub mod metadata;
13pub mod pins;
14pub mod playlists;
15pub mod search;
16
17#[derive(Debug, Clone)]
18pub struct Cache {
19    root: PathBuf,
20}
21
22impl Cache {
23    pub fn new() -> Result<Self> {
24        let root = default_root()?;
25        Ok(Self { root })
26    }
27
28    pub fn ensure_dirs(&self) -> Result<()> {
29        fs::create_dir_all(self.root())?;
30        Ok(())
31    }
32
33    pub fn root(&self) -> &Path {
34        &self.root
35    }
36
37    pub fn metadata_store(&self) -> MetadataStore {
38        MetadataStore::new(self.root.join("metadata.json"))
39    }
40
41    pub fn device_cache(&self) -> DeviceCache {
42        DeviceCache::new(self.root.join("devices.json"))
43    }
44
45    pub fn playlist_cache(&self) -> PlaylistCache {
46        PlaylistCache::new(self.root.join("playlists.json"))
47    }
48
49    pub fn pin_store(&self) -> pins::PinStore {
50        pins::PinStore::new(self.root.join("pins.json"))
51    }
52
53    pub fn search_store(&self) -> search::SearchStore {
54        search::SearchStore::new(self.root.join("search.json"))
55    }
56}
57
58fn default_root() -> Result<PathBuf> {
59    if let Ok(custom) = env::var("SPOTIFY_CLI_CACHE_DIR") {
60        return Ok(PathBuf::from(custom));
61    }
62
63    if let Ok(xdg_cache) = env::var("XDG_CACHE_HOME") {
64        return Ok(PathBuf::from(xdg_cache).join("spotify-cli"));
65    }
66
67    let home = env::var("HOME")?;
68    Ok(PathBuf::from(home).join(".cache").join("spotify-cli"))
69}
70
71#[cfg(test)]
72mod tests {
73    use super::default_root;
74    use std::sync::Mutex;
75
76    static ENV_LOCK: Mutex<()> = Mutex::new(());
77
78    fn set_env(key: &str, value: &str) {
79        unsafe {
80            std::env::set_var(key, value);
81        }
82    }
83
84    fn remove_env(key: &str) {
85        unsafe {
86            std::env::remove_var(key);
87        }
88    }
89
90    fn restore_env(key: &str, value: Option<String>) {
91        match value {
92            Some(value) => set_env(key, &value),
93            None => remove_env(key),
94        }
95    }
96
97    #[test]
98    fn default_root_uses_custom_dir() {
99        let _lock = ENV_LOCK.lock().unwrap();
100        let prev_cache = std::env::var("SPOTIFY_CLI_CACHE_DIR").ok();
101        let prev_xdg = std::env::var("XDG_CACHE_HOME").ok();
102        set_env("SPOTIFY_CLI_CACHE_DIR", "/tmp/custom-cache");
103        remove_env("XDG_CACHE_HOME");
104        let root = default_root().expect("root");
105        assert_eq!(root.to_string_lossy(), "/tmp/custom-cache");
106        restore_env("SPOTIFY_CLI_CACHE_DIR", prev_cache);
107        restore_env("XDG_CACHE_HOME", prev_xdg);
108    }
109
110    #[test]
111    fn default_root_uses_xdg_cache() {
112        let _lock = ENV_LOCK.lock().unwrap();
113        let prev_cache = std::env::var("SPOTIFY_CLI_CACHE_DIR").ok();
114        let prev_xdg = std::env::var("XDG_CACHE_HOME").ok();
115        remove_env("SPOTIFY_CLI_CACHE_DIR");
116        set_env("XDG_CACHE_HOME", "/tmp/xdg-cache");
117        let root = default_root().expect("root");
118        assert_eq!(root.to_string_lossy(), "/tmp/xdg-cache/spotify-cli");
119        restore_env("SPOTIFY_CLI_CACHE_DIR", prev_cache);
120        restore_env("XDG_CACHE_HOME", prev_xdg);
121    }
122
123    #[test]
124    fn default_root_uses_home_cache() {
125        let _lock = ENV_LOCK.lock().unwrap();
126        let prev_cache = std::env::var("SPOTIFY_CLI_CACHE_DIR").ok();
127        let prev_xdg = std::env::var("XDG_CACHE_HOME").ok();
128        let prev_home = std::env::var("HOME").ok();
129        remove_env("SPOTIFY_CLI_CACHE_DIR");
130        remove_env("XDG_CACHE_HOME");
131        set_env("HOME", "/tmp/home");
132        let root = default_root().expect("root");
133        assert_eq!(root.to_string_lossy(), "/tmp/home/.cache/spotify-cli");
134        restore_env("SPOTIFY_CLI_CACHE_DIR", prev_cache);
135        restore_env("XDG_CACHE_HOME", prev_xdg);
136        restore_env("HOME", prev_home);
137    }
138}