quick_flash/
credentials_manager.rs1use crate::credentials::Credentials;
2use anyhow::{self, Context};
3use chrono::Utc;
4use std::{
5 hash::{DefaultHasher, Hash, Hasher},
6 path::PathBuf,
7};
8
9pub struct CredentialsManager {
10 base_path: PathBuf,
11}
12
13impl CredentialsManager {
14 pub fn new(base_path: PathBuf) -> Self {
15 CredentialsManager { base_path }
16 }
17
18 pub fn get_all(&self) -> anyhow::Result<Vec<Credentials>> {
19 if !self.base_path.exists() {
20 return Ok(vec![]);
21 }
22
23 self.base_path
24 .read_dir()
25 .context("Failed to read from credentials directory")?
26 .map(|entry| {
27 let path = entry?.path();
28 Credentials::read_from_path(&path)
29 })
30 .collect()
31 }
32
33 pub fn remove(&self, user_storage_name: &str) -> anyhow::Result<()> {
34 self.base_path
35 .read_dir()
36 .context("Failed to read from credentials directory")?
37 .find(|entry| {
38 let path = entry.as_ref().map_or_else(|_| PathBuf::new(), |e| e.path());
39 Credentials::read_from_path(&path)
40 .ok()
41 .map_or(false, |c| c.user_storage_name == user_storage_name)
42 })
43 .context("Credentials not found")?
44 .and_then(|path| std::fs::remove_file(path.path()))
45 .context("Failed to remove credentials file")?;
46 Ok(())
47 }
48
49 pub fn add(&self, creds: Credentials) -> anyhow::Result<()> {
50 if !self.base_path.exists() {
51 std::fs::create_dir_all(&self.base_path)
52 .context("Failed to create credentials directory")?;
53 }
54
55 if creds.user_storage_name.is_empty() {
56 anyhow::bail!("User storage name cannot be empty");
57 }
58
59 self.get_all().and_then(|existing_creds| {
61 if existing_creds
62 .iter()
63 .any(|c| c.user_storage_name == creds.user_storage_name)
64 {
65 anyhow::bail!("Credentials with the same name already exist");
66 }
67
68 let mut hasher = DefaultHasher::new();
69 creds.hash(&mut hasher);
70
71 let name = format!(
72 "{}_{:0x}.toml",
73 Utc::now().format("%Y-%m-%d_%H-%M-%S"),
74 hasher.finish()
75 );
76 let path = self.base_path.join(name);
77 creds.write_to_path(&path)
78 })
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use tempfile::tempdir;
86
87 #[test]
88 fn test_credentials_manager() {
89 let temp_dir = tempdir().unwrap();
90 let creds_dir = temp_dir.path().join("creds");
91 let creds_manager = CredentialsManager::new(creds_dir.clone());
92 assert_eq!(creds_manager.get_all().unwrap().len(), 0);
93
94 assert_eq!(
95 creds_manager.remove("test").err().unwrap().to_string(),
96 "Failed to read from credentials directory"
97 );
98
99 let creds = Credentials::new_r2(
100 "test".to_string(),
101 "storage_name".to_string(),
102 "account_id".to_string(),
103 "access_key".to_string(),
104 "secret_key".to_string(),
105 );
106
107 creds_manager.add(creds.clone()).unwrap();
108 let all_creds = creds_manager.get_all().unwrap();
109 assert_eq!(all_creds.len(), 1);
110 assert_eq!(all_creds[0], creds);
111
112 let creds2 = Credentials::new_r2(
113 "test2".to_string(),
114 "storage_name".to_string(),
115 "account_id".to_string(),
116 "access_key".to_string(),
117 "secret_key".to_string(),
118 );
119
120 creds_manager.add(creds2.clone()).unwrap();
121 let all_creds = creds_manager.get_all().unwrap();
122 assert_eq!(all_creds.len(), 2);
123 assert!(all_creds.contains(&creds));
124 assert!(all_creds.contains(&creds2));
125
126 creds_manager.remove("test").unwrap();
127 let all_creds = creds_manager.get_all().unwrap();
128 assert_eq!(all_creds.len(), 1);
129 assert_eq!(all_creds[0].user_storage_name, "test2");
130
131 creds_manager.remove("test2").unwrap();
132 let all_creds = creds_manager.get_all().unwrap();
133 assert_eq!(all_creds.len(), 0);
134
135 assert_eq!(
136 creds_manager.remove("test").err().unwrap().to_string(),
137 "Credentials not found"
138 );
139 assert_eq!(
140 creds_manager.remove("test2").err().unwrap().to_string(),
141 "Credentials not found"
142 );
143 assert_eq!(creds_manager.get_all().unwrap().len(), 0);
144 }
145}