1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use crate::tools::get_home;
use anyhow::{anyhow, Result};
use std::{
    fs,
    path::{Path, PathBuf},
    time::{Duration, SystemTime},
};

// Load the response from a cache file ~/.ssh/vault/keys/<key>
/// # Errors
/// Return an error if the cache is older than 30 days
pub fn get(key: &str) -> Result<String> {
    let cache = get_cache_path(key)?;
    if cache.exists() {
        let metadata = fs::metadata(&cache);
        let last_modified = metadata.map_or_else(
            |_| SystemTime::now(),
            |meta| meta.modified().unwrap_or_else(|_| SystemTime::now()),
        );

        // Calculate the duration since the file was last modified
        let duration_since_modified = SystemTime::now()
            .duration_since(last_modified)
            .unwrap_or(Duration::from_secs(0));

        // Return an error if the cache is older than 30 days
        if duration_since_modified > Duration::from_secs(30 * 24 * 60 * 60) {
            Err(anyhow!("cache expired"))
        } else {
            Ok(fs::read_to_string(cache)?)
        }
    } else {
        Err(anyhow!("cache not found"))
    }
}

/// Save the response to a cache file ~/.ssh/vault/keys/<key>
/// # Errors
/// Return an error if the cache file can't be created
pub fn put(key: &str, response: &str) -> Result<()> {
    let cache = get_cache_path(key)?;
    // Create parent directories if they don't exist
    if let Some(parent_dir) = std::path::Path::new(&cache).parent() {
        fs::create_dir_all(parent_dir)?;
    }
    Ok(fs::write(cache, response)?)
}

/// Get the path to the cache file ~/.ssh/vault/keys/<key>
/// # Errors
/// Return an error if we can't get the path to the cache file
fn get_cache_path(key: &str) -> Result<PathBuf> {
    let ssh_vault = get_ssh_vault_path()?;
    Ok(ssh_vault.join("keys").join(key))
}

/// Get the path to the ssh-vault directory ~/.ssh/vault
/// # Errors
/// Return an error if we can't get the path to the ssh-vault directory
fn get_ssh_vault_path() -> Result<PathBuf> {
    let home = get_home()?;
    Ok(Path::new(&home).join(".ssh").join("vault"))
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;

    #[test]
    fn test_get_cache_path() {
        let cache = get_cache_path("test").unwrap();
        assert_eq!(cache.is_dir(), false);
        assert_eq!(
            cache.to_str(),
            get_home()
                .unwrap()
                .join(".ssh")
                .join("vault")
                .join("keys")
                .join("test")
                .to_str()
        );
    }

    #[test]
    fn test_get_ssh_vault_path() {
        let ssh_vault = get_ssh_vault_path().unwrap();
        assert_eq!(ssh_vault.is_file(), false);
        assert_eq!(
            ssh_vault.to_str(),
            get_home().unwrap().join(".ssh").join("vault").to_str()
        );
    }

    #[test]
    fn test_put() {
        let cache = get_cache_path("test-2").unwrap();
        put("test-2", "test").unwrap();

        assert_eq!(cache.is_file(), true);
        assert_eq!(cache.is_dir(), false);
        assert_eq!(cache.exists(), true);
        assert_eq!(
            cache.to_str(),
            get_home()
                .unwrap()
                .join(".ssh")
                .join("vault")
                .join("keys")
                .join("test-2")
                .to_str()
        );
        fs::remove_file(cache).unwrap();
    }

    #[test]
    fn test_get() {
        let cache = get_cache_path("test-3").unwrap();
        put("test-3", "test").unwrap();
        let response = get("test-3").unwrap();
        assert_eq!(response, "test");
        fs::remove_file(cache).unwrap();
    }
}