1use anyhow::{Context, Result};
17use chrono::{DateTime, Utc};
18use serde::{Deserialize, Serialize};
19use std::fs;
20use std::path::{Path, PathBuf};
21
22#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
24#[serde(default, deny_unknown_fields)]
25pub struct RepoTrust {
26 pub allow_project_commands: bool,
28
29 pub trusted_at: Option<DateTime<Utc>>,
31}
32
33impl RepoTrust {
34 pub fn is_trusted(&self) -> bool {
35 self.allow_project_commands
36 }
37}
38
39pub fn project_trust_path(repo_root: &Path) -> PathBuf {
41 repo_root.join(".ralph").join("trust.jsonc")
42}
43
44pub fn load_repo_trust(repo_root: &Path) -> Result<RepoTrust> {
46 let path = project_trust_path(repo_root);
47 if !path.exists() {
48 return Ok(RepoTrust::default());
49 }
50
51 let raw = fs::read_to_string(&path).with_context(|| format!("read {}", path.display()))?;
52 crate::jsonc::parse_jsonc::<RepoTrust>(&raw, &format!("trust {}", path.display()))
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use tempfile::TempDir;
59
60 #[test]
61 fn project_trust_path_is_jsonc_only() {
62 let repo_root = TempDir::new().expect("temp dir");
63 assert_eq!(
64 project_trust_path(repo_root.path()),
65 repo_root.path().join(".ralph/trust.jsonc")
66 );
67 }
68
69 #[test]
70 fn load_repo_trust_ignores_legacy_json_file() {
71 let repo_root = TempDir::new().expect("temp dir");
72 let ralph_dir = repo_root.path().join(".ralph");
73 fs::create_dir_all(&ralph_dir).expect("create .ralph");
74 fs::write(
75 ralph_dir.join("trust.json"),
76 r#"{"allow_project_commands":true}"#,
77 )
78 .expect("write legacy trust file");
79
80 assert_eq!(
81 load_repo_trust(repo_root.path()).expect("load trust"),
82 RepoTrust::default()
83 );
84 }
85}