1use git::repository::RepoPath;
2use std::ops::Add;
3use sum_tree::{ContextLessSummary, Item, KeyedItem};
4use worktree::{PathKey, PathSummary};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum GitStatus {
8 Staged,
9 Unstaged,
10 Reverted,
11 Unchanged,
12}
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum JobStatus {
16 Running,
17 Finished,
18 Skipped,
19 Error,
20}
21
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct PendingOps {
24 pub repo_path: RepoPath,
25 pub ops: Vec<PendingOp>,
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub struct PendingOp {
30 pub id: PendingOpId,
31 pub git_status: GitStatus,
32 pub job_status: JobStatus,
33}
34
35#[derive(Clone, Debug)]
36pub struct PendingOpsSummary {
37 pub staged_count: usize,
38 pub staging_count: usize,
39}
40
41#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
42pub struct PendingOpId(pub u16);
43
44impl Item for PendingOps {
45 type Summary = PathSummary<PendingOpsSummary>;
46
47 fn summary(&self, _cx: ()) -> Self::Summary {
48 PathSummary {
49 max_path: self.repo_path.0.clone(),
50 item_summary: PendingOpsSummary {
51 staged_count: self.staged() as usize,
52 staging_count: self.staging() as usize,
53 },
54 }
55 }
56}
57
58impl ContextLessSummary for PendingOpsSummary {
59 fn zero() -> Self {
60 Self {
61 staged_count: 0,
62 staging_count: 0,
63 }
64 }
65
66 fn add_summary(&mut self, summary: &Self) {
67 self.staged_count += summary.staged_count;
68 self.staging_count += summary.staging_count;
69 }
70}
71
72impl KeyedItem for PendingOps {
73 type Key = PathKey;
74
75 fn key(&self) -> Self::Key {
76 PathKey(self.repo_path.0.clone())
77 }
78}
79
80impl Add<u16> for PendingOpId {
81 type Output = PendingOpId;
82
83 fn add(self, rhs: u16) -> Self::Output {
84 Self(self.0 + rhs)
85 }
86}
87
88impl From<u16> for PendingOpId {
89 fn from(id: u16) -> Self {
90 Self(id)
91 }
92}
93
94impl PendingOps {
95 pub fn new(path: &RepoPath) -> Self {
96 Self {
97 repo_path: path.clone(),
98 ops: Vec::new(),
99 }
100 }
101
102 pub fn max_id(&self) -> PendingOpId {
103 self.ops.last().map(|op| op.id).unwrap_or_default()
104 }
105
106 pub fn op_by_id(&self, id: PendingOpId) -> Option<&PendingOp> {
107 self.ops.iter().find(|op| op.id == id)
108 }
109
110 pub fn op_by_id_mut(&mut self, id: PendingOpId) -> Option<&mut PendingOp> {
111 self.ops.iter_mut().find(|op| op.id == id)
112 }
113
114 /// File is staged if the last job is finished and has status Staged.
115 pub fn staged(&self) -> bool {
116 if let Some(last) = self.ops.last() {
117 if last.git_status == GitStatus::Staged && last.job_status == JobStatus::Finished {
118 return true;
119 }
120 }
121 false
122 }
123
124 /// File is staged if the last job is not finished and has status Staged.
125 pub fn staging(&self) -> bool {
126 if let Some(last) = self.ops.last() {
127 if last.git_status == GitStatus::Staged && last.job_status != JobStatus::Finished {
128 return true;
129 }
130 }
131 false
132 }
133}
134
135impl PendingOp {
136 pub fn running(&self) -> bool {
137 self.job_status == JobStatus::Running
138 }
139
140 pub fn finished(&self) -> bool {
141 matches!(self.job_status, JobStatus::Finished | JobStatus::Skipped)
142 }
143
144 pub fn error(&self) -> bool {
145 self.job_status == JobStatus::Error
146 }
147}