Skip to main content

gitstack/
compare.rs

1//! Branch comparison feature
2//!
3//! Lists diff commits between two branches
4
5use chrono::{DateTime, Local};
6
7/// Commit information for comparison
8#[derive(Debug, Clone)]
9pub struct CompareCommit {
10    /// Commit hash (short form)
11    pub hash: String,
12    /// Commit message (first line)
13    pub message: String,
14    /// Author name
15    pub author: String,
16    /// Commit date/time
17    pub date: DateTime<Local>,
18}
19
20/// Branch comparison result
21#[derive(Debug, Clone)]
22pub struct BranchCompare {
23    /// Base branch name
24    pub base_branch: String,
25    /// Target branch name
26    pub target_branch: String,
27    /// Commits where target is ahead of base
28    pub ahead_commits: Vec<CompareCommit>,
29    /// Commits where target is behind base
30    pub behind_commits: Vec<CompareCommit>,
31    /// Merge base commit hash
32    pub merge_base: String,
33}
34
35/// Tab for comparison view
36#[derive(Debug, Clone, Copy, PartialEq, Default)]
37pub enum CompareTab {
38    #[default]
39    Ahead,
40    Behind,
41}
42
43impl CompareTab {
44    /// Toggle the tab
45    pub fn toggle(&self) -> Self {
46        match self {
47            CompareTab::Ahead => CompareTab::Behind,
48            CompareTab::Behind => CompareTab::Ahead,
49        }
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use chrono::Local;
57
58    #[test]
59    fn compare_tab_default_is_ahead() {
60        let tab = CompareTab::default();
61        assert_eq!(tab, CompareTab::Ahead);
62    }
63
64    #[test]
65    fn compare_tab_toggle_ahead_to_behind() {
66        let tab = CompareTab::Ahead;
67        assert_eq!(tab.toggle(), CompareTab::Behind);
68    }
69
70    #[test]
71    fn compare_tab_toggle_behind_to_ahead() {
72        let tab = CompareTab::Behind;
73        assert_eq!(tab.toggle(), CompareTab::Ahead);
74    }
75
76    #[test]
77    fn compare_tab_toggle_roundtrip() {
78        let tab = CompareTab::Ahead;
79        assert_eq!(tab.toggle().toggle(), CompareTab::Ahead);
80    }
81
82    #[test]
83    fn compare_tab_clone() {
84        let tab = CompareTab::Ahead;
85        let cloned = tab;
86        assert_eq!(tab, cloned);
87    }
88
89    #[test]
90    fn compare_tab_debug() {
91        let ahead = format!("{:?}", CompareTab::Ahead);
92        let behind = format!("{:?}", CompareTab::Behind);
93        assert_eq!(ahead, "Ahead");
94        assert_eq!(behind, "Behind");
95    }
96
97    #[test]
98    fn compare_commit_fields() {
99        let commit = CompareCommit {
100            hash: "abc1234".to_string(),
101            message: "fix: some bug".to_string(),
102            author: "Test User".to_string(),
103            date: Local::now(),
104        };
105        assert_eq!(commit.hash, "abc1234");
106        assert_eq!(commit.message, "fix: some bug");
107        assert_eq!(commit.author, "Test User");
108    }
109
110    #[test]
111    fn branch_compare_empty_commits() {
112        let compare = BranchCompare {
113            base_branch: "main".to_string(),
114            target_branch: "feature".to_string(),
115            ahead_commits: vec![],
116            behind_commits: vec![],
117            merge_base: "deadbeef".to_string(),
118        };
119        assert_eq!(compare.base_branch, "main");
120        assert_eq!(compare.target_branch, "feature");
121        assert!(compare.ahead_commits.is_empty());
122        assert!(compare.behind_commits.is_empty());
123        assert_eq!(compare.merge_base, "deadbeef");
124    }
125
126    #[test]
127    fn branch_compare_with_commits() {
128        let now = Local::now();
129        let compare = BranchCompare {
130            base_branch: "main".to_string(),
131            target_branch: "feature/test".to_string(),
132            ahead_commits: vec![
133                CompareCommit {
134                    hash: "aaa1111".to_string(),
135                    message: "feat: add feature".to_string(),
136                    author: "Alice".to_string(),
137                    date: now,
138                },
139                CompareCommit {
140                    hash: "bbb2222".to_string(),
141                    message: "test: add tests".to_string(),
142                    author: "Bob".to_string(),
143                    date: now,
144                },
145            ],
146            behind_commits: vec![CompareCommit {
147                hash: "ccc3333".to_string(),
148                message: "fix: hotfix".to_string(),
149                author: "Charlie".to_string(),
150                date: now,
151            }],
152            merge_base: "000abcd".to_string(),
153        };
154        assert_eq!(compare.ahead_commits.len(), 2);
155        assert_eq!(compare.behind_commits.len(), 1);
156        assert_eq!(compare.ahead_commits[0].author, "Alice");
157        assert_eq!(compare.behind_commits[0].hash, "ccc3333");
158    }
159
160    #[test]
161    fn compare_commit_clone() {
162        let commit = CompareCommit {
163            hash: "abc".to_string(),
164            message: "msg".to_string(),
165            author: "author".to_string(),
166            date: Local::now(),
167        };
168        let cloned = commit.clone();
169        assert_eq!(commit.hash, cloned.hash);
170        assert_eq!(commit.message, cloned.message);
171        assert_eq!(commit.author, cloned.author);
172    }
173}