1use anyhow::Result;
7use console::style;
8
9use crate::constraints::AttemptStatus;
10use crate::AttemptTracker;
11
12#[derive(Debug, Clone)]
14pub enum AttemptSubcommand {
15 Start {
16 id: String,
17 for_issue: Option<String>,
18 description: Option<String>,
19 },
20 List {
21 active: bool,
22 failed: bool,
23 history: bool,
24 },
25 Fail {
26 id: String,
27 reason: Option<String>,
28 },
29 Verify {
30 id: String,
31 },
32 Revert {
33 id: String,
34 },
35 Cleanup,
36 Checkpoint {
37 name: String,
38 files: Vec<String>,
39 description: Option<String>,
40 },
41 Checkpoints,
42 Restore {
43 name: String,
44 },
45}
46
47pub fn execute_attempt(subcommand: AttemptSubcommand) -> Result<()> {
49 let mut tracker = AttemptTracker::load_or_create();
50
51 match subcommand {
52 AttemptSubcommand::Start {
53 id,
54 for_issue,
55 description,
56 } => {
57 tracker.start_attempt(&id, for_issue.as_deref(), description.as_deref());
58 tracker.save()?;
59 println!("{} Started attempt: {}", style("✓").green(), id);
60 }
61
62 AttemptSubcommand::List {
63 active,
64 failed,
65 history,
66 } => {
67 if history {
68 println!("{}", style("Attempt History:").bold());
69 for entry in &tracker.history {
70 let status_color = match entry.status {
71 AttemptStatus::Verified => style("✓").green(),
72 AttemptStatus::Failed => style("✗").red(),
73 AttemptStatus::Reverted => style("↩").yellow(),
74 _ => style("?").dim(),
75 };
76 println!(
77 " {} {} - {:?} ({} files)",
78 status_color, entry.id, entry.status, entry.files_modified
79 );
80 }
81 } else {
82 println!("{}", style("Active Attempts:").bold());
83 for attempt in tracker.attempts.values() {
84 if active && attempt.status != AttemptStatus::Active {
85 continue;
86 }
87 if failed && attempt.status != AttemptStatus::Failed {
88 continue;
89 }
90
91 let status_color = match attempt.status {
92 AttemptStatus::Active => style("●").cyan(),
93 AttemptStatus::Testing => style("◐").yellow(),
94 AttemptStatus::Failed => style("✗").red(),
95 _ => style("?").dim(),
96 };
97
98 println!(" {} {} - {:?}", status_color, attempt.id, attempt.status);
99 if let Some(issue) = &attempt.for_issue {
100 println!(" For: {}", issue);
101 }
102 println!(" Files: {}", attempt.files.len());
103 }
104 }
105 }
106
107 AttemptSubcommand::Fail { id, reason } => {
108 tracker.fail_attempt(&id, reason.as_deref())?;
109 tracker.save()?;
110 println!("{} Marked attempt as failed: {}", style("✗").red(), id);
111 }
112
113 AttemptSubcommand::Verify { id } => {
114 tracker.verify_attempt(&id)?;
115 tracker.save()?;
116 println!("{} Verified attempt: {}", style("✓").green(), id);
117 }
118
119 AttemptSubcommand::Revert { id } => {
120 let actions = tracker.revert_attempt(&id)?;
121 println!("{} Reverted attempt: {}", style("↩").yellow(), id);
122 for action in &actions {
123 println!(" {} {}", style(&action.action).dim(), action.file);
124 }
125 }
126
127 AttemptSubcommand::Cleanup => {
128 let actions = tracker.cleanup_failed()?;
129 println!(
130 "{} Cleaned up {} files from failed attempts",
131 style("✓").green(),
132 actions.len()
133 );
134 }
135
136 AttemptSubcommand::Checkpoint {
137 name,
138 files,
139 description,
140 } => {
141 let file_refs: Vec<&str> = files.iter().map(|s| s.as_str()).collect();
142 tracker.create_checkpoint(&name, &file_refs, description.as_deref())?;
143 println!(
144 "{} Created checkpoint: {} ({} files)",
145 style("✓").green(),
146 name,
147 files.len()
148 );
149 }
150
151 AttemptSubcommand::Checkpoints => {
152 println!("{}", style("Checkpoints:").bold());
153 for (name, cp) in &tracker.checkpoints {
154 println!(
155 " {} - {} files, created {}",
156 style(name).cyan(),
157 cp.files.len(),
158 cp.created_at.format("%Y-%m-%d %H:%M")
159 );
160 }
161 }
162
163 AttemptSubcommand::Restore { name } => {
164 let actions = tracker.restore_checkpoint(&name)?;
165 println!("{} Restored checkpoint: {}", style("↩").yellow(), name);
166 for action in &actions {
167 println!(" {} {}", style(&action.action).dim(), action.file);
168 }
169 }
170 }
171
172 Ok(())
173}