aico/
trust.rs

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}