dot_agent_core/
metadata.rs1use std::collections::HashMap;
2use std::fs;
3use std::path::Path;
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use sha2::{Digest, Sha256};
8
9use crate::error::Result;
10
11const META_FILENAME: &str = ".dot-agent-meta.toml";
12
13#[derive(Debug, Serialize, Deserialize)]
14pub struct Metadata {
15 pub installed: InstalledInfo,
16 pub files: HashMap<String, String>,
17}
18
19#[derive(Debug, Serialize, Deserialize)]
20pub struct InstalledInfo {
21 pub installed_at: DateTime<Utc>,
22 pub profiles: Vec<String>,
23 pub base_dir: String,
24}
25
26impl Metadata {
27 pub fn new(base_dir: &Path) -> Self {
28 Self {
29 installed: InstalledInfo {
30 installed_at: Utc::now(),
31 profiles: Vec::new(),
32 base_dir: base_dir.display().to_string(),
33 },
34 files: HashMap::new(),
35 }
36 }
37
38 pub fn load(target: &Path) -> Result<Option<Self>> {
39 let meta_path = target.join(META_FILENAME);
40 if !meta_path.exists() {
41 return Ok(None);
42 }
43 let content = fs::read_to_string(&meta_path)?;
44 let meta: Metadata = toml::from_str(&content)?;
45 Ok(Some(meta))
46 }
47
48 pub fn save(&self, target: &Path) -> Result<()> {
49 let meta_path = target.join(META_FILENAME);
50 let content = toml::to_string_pretty(self)?;
51 fs::write(&meta_path, content)?;
52 Ok(())
53 }
54
55 pub fn add_profile(&mut self, name: &str) {
56 if !self.installed.profiles.contains(&name.to_string()) {
57 self.installed.profiles.push(name.to_string());
58 }
59 }
60
61 pub fn remove_profile(&mut self, name: &str) {
62 self.installed.profiles.retain(|p| p != name);
63 }
64
65 pub fn add_file(&mut self, path: &str, hash: &str) {
66 self.files.insert(path.to_string(), hash.to_string());
67 }
68
69 pub fn remove_file(&mut self, path: &str) {
70 self.files.remove(path);
71 }
72
73 pub fn get_file_hash(&self, path: &str) -> Option<&String> {
74 self.files.get(path)
75 }
76}
77
78pub fn compute_hash(content: &[u8]) -> String {
79 let mut hasher = Sha256::new();
80 hasher.update(content);
81 let result = hasher.finalize();
82 format!("sha256:{}", hex::encode(result))
83}
84
85pub fn compute_file_hash(path: &Path) -> Result<String> {
86 let content = fs::read(path)?;
87 Ok(compute_hash(&content))
88}