1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum RevertPlan {
19 NoChanges,
21 Proceed,
23}
24
25pub fn plan_revert(change_count: usize) -> RevertPlan {
29 if revert_has_no_changes(change_count) {
30 RevertPlan::NoChanges
31 } else {
32 RevertPlan::Proceed
33 }
34}
35
36pub fn revert_has_no_changes(change_count: usize) -> bool {
38 change_count == 0
39}
40
41pub fn no_changes_to_revert_kind() -> &'static str {
43 "no_changes_to_revert"
44}
45
46pub fn revert_inspect_command(state_short: &str) -> String {
48 format!("heddle show {state_short}")
49}
50
51pub fn default_revert_commit_message(state_short: &str) -> String {
57 format!("Revert {state_short}")
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum RevertMessageMode {
63 Text,
65 Json,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum RevertOutcome {
72 AppliedNotCommitted,
74 Committed,
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct RevertSuccessFacts<'a> {
81 pub outcome: RevertOutcome,
82 pub state_short: &'a str,
83 pub new_state_id_short: Option<&'a str>,
85}
86
87pub fn revert_success_message(facts: &RevertSuccessFacts<'_>, mode: RevertMessageMode) -> String {
95 match (facts.outcome, mode) {
96 (RevertOutcome::AppliedNotCommitted, RevertMessageMode::Text) => {
97 format!("Reverted {} (not committed)", facts.state_short)
98 }
99 (RevertOutcome::AppliedNotCommitted, RevertMessageMode::Json) => {
100 "Changes applied to worktree (not committed)".to_string()
101 }
102 (RevertOutcome::Committed, RevertMessageMode::Text) => {
103 let new_id = facts.new_state_id_short.unwrap_or("");
104 format!("Reverted {} as {}", facts.state_short, new_id)
105 }
106 (RevertOutcome::Committed, RevertMessageMode::Json) => {
107 let new_id = facts.new_state_id_short.unwrap_or("");
108 format!("Created revert state {new_id}")
109 }
110 }
111}
112
113pub fn no_changes_to_revert_summary(state_short: &str) -> String {
115 format!("No changes to revert in state {state_short}")
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn empty_diff_gate() {
124 assert_eq!(plan_revert(0), RevertPlan::NoChanges);
125 assert_eq!(plan_revert(1), RevertPlan::Proceed);
126 assert_eq!(plan_revert(3), RevertPlan::Proceed);
127 assert!(revert_has_no_changes(0));
128 assert!(!revert_has_no_changes(2));
129 assert_eq!(no_changes_to_revert_kind(), "no_changes_to_revert");
130 assert_eq!(revert_inspect_command("abc1234"), "heddle show abc1234");
131 assert!(no_changes_to_revert_summary("abc").contains("abc"));
132 }
133
134 #[test]
135 fn default_and_success_messages() {
136 assert_eq!(
137 default_revert_commit_message("hs-deadbee"),
138 "Revert hs-deadbee"
139 );
140
141 let no_commit = RevertSuccessFacts {
142 outcome: RevertOutcome::AppliedNotCommitted,
143 state_short: "hs-aaaa",
144 new_state_id_short: None,
145 };
146 assert_eq!(
147 revert_success_message(&no_commit, RevertMessageMode::Text),
148 "Reverted hs-aaaa (not committed)"
149 );
150 assert_eq!(
151 revert_success_message(&no_commit, RevertMessageMode::Json),
152 "Changes applied to worktree (not committed)"
153 );
154
155 let committed = RevertSuccessFacts {
156 outcome: RevertOutcome::Committed,
157 state_short: "hs-aaaa",
158 new_state_id_short: Some("hs-bbbb"),
159 };
160 assert_eq!(
161 revert_success_message(&committed, RevertMessageMode::Text),
162 "Reverted hs-aaaa as hs-bbbb"
163 );
164 assert_eq!(
165 revert_success_message(&committed, RevertMessageMode::Json),
166 "Created revert state hs-bbbb"
167 );
168 }
169}