1use crate::git::git_interop::{create_new_write_ref, delete_reference, get_write_refs};
2use crate::status::gather_pending_status;
3use anyhow::{Context, Result};
4use std::io::{self, Write};
5
6#[derive(Debug)]
8pub struct ResetPlan {
9 pub refs_to_delete: Vec<String>,
11
12 pub measurement_count: usize,
14
15 pub commit_count: usize,
17}
18
19pub fn reset_measurements(dry_run: bool, force: bool) -> Result<()> {
21 let new_write_ref = create_new_write_ref().context("Failed to create fresh write ref")?;
25
26 let plan = plan_reset(&new_write_ref)?;
28
29 if plan.refs_to_delete.is_empty() {
31 println!("No pending measurements to reset.");
32 return Ok(());
33 }
34
35 display_reset_plan(&plan)?;
37
38 if !dry_run && !force && !confirm_reset()? {
40 println!("Reset cancelled.");
41 return Ok(());
42 }
43
44 if dry_run {
46 println!();
47 println!("Dry run - no changes made.");
48 } else {
49 execute_reset(&plan)?;
50 println!();
51 let ref_word = if plan.refs_to_delete.len() == 1 {
52 "ref"
53 } else {
54 "refs"
55 };
56 println!(
57 "Reset complete. {} write {} deleted.",
58 plan.refs_to_delete.len(),
59 ref_word
60 );
61 }
62
63 Ok(())
64}
65
66fn plan_reset(new_write_ref: &str) -> Result<ResetPlan> {
70 let refs = get_write_refs()?;
72
73 let refs_to_delete: Vec<String> = refs
75 .into_iter()
76 .map(|(refname, _)| refname)
77 .filter(|refname| refname != new_write_ref)
78 .collect();
79
80 if refs_to_delete.is_empty() {
81 return Ok(ResetPlan {
82 refs_to_delete: vec![],
83 measurement_count: 0,
84 commit_count: 0,
85 });
86 }
87
88 let status = gather_pending_status(false)?;
90
91 Ok(ResetPlan {
92 refs_to_delete,
93 measurement_count: status.measurement_count,
94 commit_count: status.commit_count,
95 })
96}
97
98fn execute_reset(plan: &ResetPlan) -> Result<()> {
100 for ref_name in &plan.refs_to_delete {
103 delete_reference(ref_name)
104 .with_context(|| format!("Failed to delete reference: {}", ref_name))?;
105 }
106
107 Ok(())
108}
109
110fn display_reset_plan(plan: &ResetPlan) -> Result<()> {
112 let status = gather_pending_status(false)?;
114
115 println!("Will reset:");
116 let commit_word = if plan.commit_count == 1 {
117 "commit"
118 } else {
119 "commits"
120 };
121 println!(" {} {} with measurements", plan.commit_count, commit_word);
122 let measurement_word = if status.measurement_names.len() == 1 {
123 "measurement"
124 } else {
125 "measurements"
126 };
127 println!(
128 " {} unique {}",
129 status.measurement_names.len(),
130 measurement_word
131 );
132 println!();
133
134 if !status.measurement_names.is_empty() {
135 println!("Measurement names:");
136 let mut sorted_names: Vec<_> = status.measurement_names.iter().collect();
137 sorted_names.sort();
138 for name in sorted_names {
139 println!(" - {}", name);
140 }
141 println!();
142 }
143
144 Ok(())
145}
146
147fn confirm_reset() -> Result<bool> {
149 print!("Are you sure you want to discard these pending measurements? [y/N] ");
150 io::stdout().flush()?;
151
152 let mut input = String::new();
153 io::stdin().read_line(&mut input)?;
154
155 let response = input.trim().to_lowercase();
156 Ok(response == "y" || response == "yes")
157}