opendev_models/
file_change.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use strum::{Display, EnumString};
6use uuid::Uuid;
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Display, EnumString)]
10#[serde(rename_all = "lowercase")]
11#[strum(serialize_all = "lowercase")]
12pub enum FileChangeType {
13 Created,
14 Modified,
15 Deleted,
16 Renamed,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct FileChange {
22 #[serde(default = "generate_change_id")]
23 pub id: String,
24 #[serde(rename = "type")]
25 pub change_type: FileChangeType,
26 pub file_path: String,
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub old_path: Option<String>,
30 #[serde(default = "Utc::now", with = "crate::datetime_compat")]
31 pub timestamp: DateTime<Utc>,
32 #[serde(default)]
33 pub lines_added: u64,
34 #[serde(default)]
35 pub lines_removed: u64,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub tool_call_id: Option<String>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub session_id: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub description: Option<String>,
43}
44
45fn generate_change_id() -> String {
46 Uuid::new_v4().to_string()[..8].to_string()
47}
48
49impl FileChange {
50 pub fn new(change_type: FileChangeType, file_path: String) -> Self {
52 Self {
53 id: generate_change_id(),
54 change_type,
55 file_path,
56 old_path: None,
57 timestamp: Utc::now(),
58 lines_added: 0,
59 lines_removed: 0,
60 tool_call_id: None,
61 session_id: None,
62 description: None,
63 }
64 }
65
66 pub fn get_file_icon(&self) -> &'static str {
68 match self.change_type {
69 FileChangeType::Created => "+",
70 FileChangeType::Modified => "~",
71 FileChangeType::Deleted => "-",
72 FileChangeType::Renamed => ">",
73 }
74 }
75
76 pub fn get_status_color(&self) -> &'static str {
78 match self.change_type {
79 FileChangeType::Created => "green",
80 FileChangeType::Modified => "blue",
81 FileChangeType::Deleted => "red",
82 FileChangeType::Renamed => "orange",
83 }
84 }
85
86 pub fn get_change_summary(&self) -> String {
88 match self.change_type {
89 FileChangeType::Created => "New file".to_string(),
90 FileChangeType::Modified => {
91 if self.lines_added > 0 && self.lines_removed > 0 {
92 format!("+{} -{}", self.lines_added, self.lines_removed)
93 } else if self.lines_added > 0 {
94 format!("+{}", self.lines_added)
95 } else if self.lines_removed > 0 {
96 format!("-{}", self.lines_removed)
97 } else {
98 "Modified".to_string()
99 }
100 }
101 FileChangeType::Deleted => "Deleted".to_string(),
102 FileChangeType::Renamed => format!("Renamed -> {}", self.file_path),
103 }
104 }
105}
106
107#[cfg(test)]
108#[path = "file_change_tests.rs"]
109mod tests;