aaai_core/profile/
store.rs1use serde::{Deserialize, Serialize};
5use std::path::PathBuf;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct AuditProfile {
10 pub name: String,
12 pub before: String,
13 pub after: String,
14 pub definition: Option<String>,
15 #[serde(default, skip_serializing_if = "Option::is_none")]
16 pub ignore_file: Option<String>,
17}
18
19#[derive(Debug, Clone, Default, Serialize, Deserialize)]
21pub struct ProfileStore {
22 #[serde(default)]
23 pub profiles: Vec<AuditProfile>,
24}
25
26impl ProfileStore {
27 pub fn load() -> anyhow::Result<Self> {
29 let path = profile_path()?;
30 if !path.exists() {
31 return Ok(Self::default());
32 }
33 let text = std::fs::read_to_string(&path)?;
34 Ok(serde_yaml::from_str(&text)?)
35 }
36
37 pub fn save(&self) -> anyhow::Result<()> {
39 let path = profile_path()?;
40 let yaml = serde_yaml::to_string(self)?;
41 std::fs::write(&path, yaml)?;
42 Ok(())
43 }
44
45 pub fn add(&mut self, profile: AuditProfile) {
46 if let Some(pos) = self.profiles.iter().position(|p| p.name == profile.name) {
48 self.profiles[pos] = profile;
49 } else {
50 self.profiles.push(profile);
51 }
52 }
53
54 pub fn remove(&mut self, name: &str) {
55 self.profiles.retain(|p| p.name != name);
56 }
57}
58
59fn profile_path() -> anyhow::Result<PathBuf> {
60 let base = dirs::home_dir()
61 .ok_or_else(|| anyhow::anyhow!("Cannot determine home directory"))?
62 .join(".aaai");
63 std::fs::create_dir_all(&base)?;
64 Ok(base.join("profiles.yaml"))
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn add_and_remove() {
73 let mut store = ProfileStore::default();
74 store.add(AuditProfile {
75 name: "prod".into(), before: "/before".into(),
76 after: "/after".into(), definition: None, ignore_file: None,
77 });
78 assert_eq!(store.profiles.len(), 1);
79 store.remove("prod");
80 assert!(store.profiles.is_empty());
81 }
82
83 #[test]
84 fn add_replaces_same_name() {
85 let mut store = ProfileStore::default();
86 store.add(AuditProfile {
87 name: "p".into(), before: "/a".into(),
88 after: "/b".into(), definition: None, ignore_file: None,
89 });
90 store.add(AuditProfile {
91 name: "p".into(), before: "/x".into(),
92 after: "/y".into(), definition: None, ignore_file: None,
93 });
94 assert_eq!(store.profiles.len(), 1);
95 assert_eq!(store.profiles[0].before, "/x");
96 }
97}