gitent_core/
models.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::path::PathBuf;
5use uuid::Uuid;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8pub enum ChangeType {
9    Create,
10    Modify,
11    Delete,
12    Rename,
13}
14
15impl ChangeType {
16    pub fn as_str(&self) -> &str {
17        match self {
18            ChangeType::Create => "create",
19            ChangeType::Modify => "modify",
20            ChangeType::Delete => "delete",
21            ChangeType::Rename => "rename",
22        }
23    }
24
25    pub fn parse(s: &str) -> Option<Self> {
26        match s {
27            "create" => Some(ChangeType::Create),
28            "modify" => Some(ChangeType::Modify),
29            "delete" => Some(ChangeType::Delete),
30            "rename" => Some(ChangeType::Rename),
31            _ => None,
32        }
33    }
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct Change {
38    pub id: Uuid,
39    pub timestamp: DateTime<Utc>,
40    pub change_type: ChangeType,
41    pub path: PathBuf,
42    pub old_path: Option<PathBuf>,
43    pub content_before: Option<Vec<u8>>,
44    pub content_after: Option<Vec<u8>>,
45    pub content_hash_before: Option<String>,
46    pub content_hash_after: Option<String>,
47    pub agent_id: Option<String>,
48    pub metadata: HashMap<String, String>,
49    pub session_id: Uuid,
50}
51
52impl Change {
53    pub fn new(change_type: ChangeType, path: PathBuf, session_id: Uuid) -> Self {
54        Self {
55            id: Uuid::new_v4(),
56            timestamp: Utc::now(),
57            change_type,
58            path,
59            old_path: None,
60            content_before: None,
61            content_after: None,
62            content_hash_before: None,
63            content_hash_after: None,
64            agent_id: None,
65            metadata: HashMap::new(),
66            session_id,
67        }
68    }
69
70    pub fn with_content_before(mut self, content: Vec<u8>) -> Self {
71        self.content_hash_before = Some(Self::hash_content(&content));
72        self.content_before = Some(content);
73        self
74    }
75
76    pub fn with_content_after(mut self, content: Vec<u8>) -> Self {
77        self.content_hash_after = Some(Self::hash_content(&content));
78        self.content_after = Some(content);
79        self
80    }
81
82    pub fn with_agent_id(mut self, agent_id: String) -> Self {
83        self.agent_id = Some(agent_id);
84        self
85    }
86
87    pub fn with_metadata(mut self, key: String, value: String) -> Self {
88        self.metadata.insert(key, value);
89        self
90    }
91
92    pub fn with_old_path(mut self, old_path: PathBuf) -> Self {
93        self.old_path = Some(old_path);
94        self
95    }
96
97    fn hash_content(content: &[u8]) -> String {
98        use sha2::{Digest, Sha256};
99        let mut hasher = Sha256::new();
100        hasher.update(content);
101        hex::encode(hasher.finalize())
102    }
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct Commit {
107    pub id: Uuid,
108    pub parent: Option<Uuid>,
109    pub timestamp: DateTime<Utc>,
110    pub message: String,
111    pub agent_id: String,
112    pub changes: Vec<Uuid>,
113    pub session_id: Uuid,
114    pub metadata: HashMap<String, String>,
115}
116
117impl Commit {
118    pub fn new(message: String, agent_id: String, changes: Vec<Uuid>, session_id: Uuid) -> Self {
119        Self {
120            id: Uuid::new_v4(),
121            parent: None,
122            timestamp: Utc::now(),
123            message,
124            agent_id,
125            changes,
126            session_id,
127            metadata: HashMap::new(),
128        }
129    }
130
131    pub fn with_parent(mut self, parent: Uuid) -> Self {
132        self.parent = Some(parent);
133        self
134    }
135
136    pub fn with_metadata(mut self, key: String, value: String) -> Self {
137        self.metadata.insert(key, value);
138        self
139    }
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct Session {
144    pub id: Uuid,
145    pub root_path: PathBuf,
146    pub started: DateTime<Utc>,
147    pub ended: Option<DateTime<Utc>>,
148    pub active: bool,
149    pub ignore_patterns: Vec<String>,
150}
151
152impl Session {
153    pub fn new(root_path: PathBuf) -> Self {
154        Self {
155            id: Uuid::new_v4(),
156            root_path,
157            started: Utc::now(),
158            ended: None,
159            active: true,
160            ignore_patterns: vec![
161                ".git".to_string(),
162                "target".to_string(),
163                "node_modules".to_string(),
164                ".gitent".to_string(),
165            ],
166        }
167    }
168
169    pub fn with_ignore_patterns(mut self, patterns: Vec<String>) -> Self {
170        self.ignore_patterns = patterns;
171        self
172    }
173
174    pub fn end(&mut self) {
175        self.active = false;
176        self.ended = Some(Utc::now());
177    }
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct CommitInfo {
182    pub commit: Commit,
183    pub change_count: usize,
184    pub files_affected: Vec<PathBuf>,
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190
191    #[test]
192    fn test_change_creation() {
193        let session_id = Uuid::new_v4();
194        let change = Change::new(ChangeType::Create, PathBuf::from("test.txt"), session_id);
195
196        assert_eq!(change.change_type, ChangeType::Create);
197        assert_eq!(change.path, PathBuf::from("test.txt"));
198        assert_eq!(change.session_id, session_id);
199    }
200
201    #[test]
202    fn test_change_with_content() {
203        let session_id = Uuid::new_v4();
204        let content = b"Hello, World!".to_vec();
205        let change = Change::new(ChangeType::Create, PathBuf::from("test.txt"), session_id)
206            .with_content_after(content.clone());
207
208        assert!(change.content_after.is_some());
209        assert!(change.content_hash_after.is_some());
210        assert_eq!(change.content_after.unwrap(), content);
211    }
212
213    #[test]
214    fn test_commit_creation() {
215        let session_id = Uuid::new_v4();
216        let change_ids = vec![Uuid::new_v4(), Uuid::new_v4()];
217        let commit = Commit::new(
218            "Initial commit".to_string(),
219            "test-agent".to_string(),
220            change_ids.clone(),
221            session_id,
222        );
223
224        assert_eq!(commit.message, "Initial commit");
225        assert_eq!(commit.agent_id, "test-agent");
226        assert_eq!(commit.changes, change_ids);
227    }
228
229    #[test]
230    fn test_session_creation() {
231        let session = Session::new(PathBuf::from("/test/path"));
232
233        assert_eq!(session.root_path, PathBuf::from("/test/path"));
234        assert!(session.active);
235        assert!(session.ended.is_none());
236        assert!(!session.ignore_patterns.is_empty());
237    }
238}