ssh_vault/
cache.rs

1use crate::tools::get_home;
2use anyhow::{Result, anyhow};
3use std::{
4    fs,
5    path::{Path, PathBuf},
6    time::{Duration, SystemTime},
7};
8
9// Load the response from a cache file ~/.ssh/vault/keys/`<key>`
10/// # Errors
11/// Return an error if the cache is older than 30 days
12pub fn get(key: &str) -> Result<String> {
13    let cache = get_cache_path(key)?;
14    if cache.exists() {
15        let metadata = fs::metadata(&cache);
16        let last_modified = metadata.map_or_else(
17            |_| SystemTime::now(),
18            |meta| meta.modified().unwrap_or_else(|_| SystemTime::now()),
19        );
20
21        // Calculate the duration since the file was last modified
22        let duration_since_modified = SystemTime::now()
23            .duration_since(last_modified)
24            .unwrap_or(Duration::from_secs(0));
25
26        // Return an error if the cache is older than 30 days
27        if duration_since_modified > Duration::from_secs(30 * 24 * 60 * 60) {
28            Err(anyhow!("cache expired"))
29        } else {
30            Ok(fs::read_to_string(cache)?)
31        }
32    } else {
33        Err(anyhow!("cache not found"))
34    }
35}
36
37/// Save the response to a cache file ~/.ssh/vault/keys/`<key>`
38/// # Errors
39/// Return an error if the cache file can't be created
40pub fn put(key: &str, response: &str) -> Result<()> {
41    let cache = get_cache_path(key)?;
42    // Create parent directories if they don't exist
43    if let Some(parent_dir) = std::path::Path::new(&cache).parent() {
44        fs::create_dir_all(parent_dir)?;
45    }
46    Ok(fs::write(cache, response)?)
47}
48
49/// Get the path to the cache file ~/.ssh/vault/keys/`<key>`
50/// # Errors
51/// Return an error if we can't get the path to the cache file
52fn get_cache_path(key: &str) -> Result<PathBuf> {
53    let ssh_vault = get_ssh_vault_path()?;
54    Ok(ssh_vault.join("keys").join(key))
55}
56
57/// Get the path to the ssh-vault directory ~/.ssh/vault
58/// # Errors
59/// Return an error if we can't get the path to the ssh-vault directory
60fn get_ssh_vault_path() -> Result<PathBuf> {
61    let home = get_home()?;
62    Ok(Path::new(&home).join(".ssh").join("vault"))
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use std::fs;
69
70    #[test]
71    fn test_get_cache_path() {
72        let cache = get_cache_path("test").unwrap();
73        assert_eq!(cache.is_dir(), false);
74        assert_eq!(
75            cache.to_str(),
76            get_home()
77                .unwrap()
78                .join(".ssh")
79                .join("vault")
80                .join("keys")
81                .join("test")
82                .to_str()
83        );
84    }
85
86    #[test]
87    fn test_get_ssh_vault_path() {
88        let ssh_vault = get_ssh_vault_path().unwrap();
89        assert_eq!(ssh_vault.is_file(), false);
90        assert_eq!(
91            ssh_vault.to_str(),
92            get_home().unwrap().join(".ssh").join("vault").to_str()
93        );
94    }
95
96    #[test]
97    fn test_put() {
98        let cache = get_cache_path("test-2").unwrap();
99        put("test-2", "test").unwrap();
100
101        assert_eq!(cache.is_file(), true);
102        assert_eq!(cache.is_dir(), false);
103        assert_eq!(cache.exists(), true);
104        assert_eq!(
105            cache.to_str(),
106            get_home()
107                .unwrap()
108                .join(".ssh")
109                .join("vault")
110                .join("keys")
111                .join("test-2")
112                .to_str()
113        );
114        fs::remove_file(cache).unwrap();
115    }
116
117    #[test]
118    fn test_get() {
119        let cache = get_cache_path("test-3").unwrap();
120        put("test-3", "test").unwrap();
121        let response = get("test-3").unwrap();
122        assert_eq!(response, "test");
123        fs::remove_file(cache).unwrap();
124    }
125}