1use crate::exceptions::AicoError;
2use crate::fs::atomic_write_json;
3use serde::{Deserialize, Serialize};
4use std::collections::HashSet;
5use std::env;
6use std::fs;
7use std::path::{Path, PathBuf};
8
9#[derive(Serialize, Deserialize, Default)]
10struct TrustConfig {
11 trusted_projects: Vec<String>,
12}
13
14fn get_config_dir() -> PathBuf {
15 if let Ok(xdg_config) = env::var("XDG_CONFIG_HOME") {
16 PathBuf::from(xdg_config).join("aico")
17 } else {
18 #[cfg(windows)]
19 {
20 PathBuf::from(env::var("APPDATA").unwrap_or_else(|_| ".".into())).join("aico")
21 }
22 #[cfg(not(windows))]
23 {
24 if let Ok(home) = env::var("HOME") {
25 PathBuf::from(home).join(".config").join("aico")
26 } else {
27 PathBuf::from(".").join(".config").join("aico")
28 }
29 }
30 }
31}
32
33pub fn get_trust_file() -> PathBuf {
34 get_config_dir().join("trust.json")
35}
36
37fn load_trusted_paths() -> HashSet<String> {
38 let trust_file = get_trust_file();
39 if !trust_file.exists() {
40 return HashSet::new();
41 }
42
43 match fs::read_to_string(trust_file) {
44 Ok(content) => {
45 let config: TrustConfig = serde_json::from_str(&content).unwrap_or_default();
46 config.trusted_projects.into_iter().collect()
47 }
48 Err(_) => HashSet::new(),
49 }
50}
51
52fn save_trusted_paths(paths: HashSet<String>) -> Result<(), AicoError> {
53 let trust_file = get_trust_file();
54 if let Some(parent) = trust_file.parent() {
55 fs::create_dir_all(parent)?;
56 }
57 let mut vec_paths: Vec<String> = paths.into_iter().collect();
58 vec_paths.sort();
59
60 let config = TrustConfig {
61 trusted_projects: vec_paths,
62 };
63
64 atomic_write_json(&trust_file, &config)?;
65
66 #[cfg(unix)]
67 {
68 use std::os::unix::fs::PermissionsExt;
69 if let Ok(meta) = fs::metadata(&trust_file) {
70 let mut perms = meta.permissions();
71 perms.set_mode(0o600);
72 let _ = fs::set_permissions(&trust_file, perms);
73 }
74 }
75
76 Ok(())
77}
78
79pub fn is_project_trusted(path: &Path) -> bool {
80 let resolved = match fs::canonicalize(path) {
81 Ok(p) => p.to_string_lossy().to_string(),
82 Err(_) => return false,
83 };
84 let trusted = load_trusted_paths();
85 trusted.contains(&resolved)
86}
87
88pub fn trust_project(path: &Path) -> Result<(), AicoError> {
89 let resolved = fs::canonicalize(path)?;
90 let resolved_str = resolved.to_string_lossy().to_string();
91 let mut trusted = load_trusted_paths();
92 trusted.insert(resolved_str);
93 save_trusted_paths(trusted)
94}
95
96pub fn untrust_project(path: &Path) -> Result<bool, AicoError> {
97 let resolved = fs::canonicalize(path)?;
98 let resolved_str = resolved.to_string_lossy().to_string();
99 let mut trusted = load_trusted_paths();
100 let removed = trusted.remove(&resolved_str);
101 if removed {
102 save_trusted_paths(trusted)?;
103 }
104 Ok(removed)
105}
106
107pub fn list_trusted_projects() -> Vec<String> {
108 let mut list: Vec<String> = load_trusted_paths().into_iter().collect();
109 list.sort();
110 list
111}