1use super::{load_manifest, manifest_path, rhack_dir, Cmd, PATCH_TABLE_NAME, REGISTRY_TABLE_NAME};
2
3use std::fs;
4use std::path::PathBuf;
5
6use anyhow::{anyhow, Result};
7use clap::Parser;
8use toml_edit::Item;
9
10#[derive(Parser, Debug)]
12pub struct Undo {
13 #[clap(short, long)]
15 verbose: bool,
16}
17
18impl Cmd for Undo {
19 fn run(&self) -> Result<()> {
20 let manifest_path = match manifest_path() {
21 Ok(p) => p,
22 Err(err) => return Err(err),
23 };
24
25 let manifest = match load_manifest(&manifest_path) {
26 Ok(m) => m,
27 Err(err) => return Err(err),
28 };
29
30 if matches!(manifest[PATCH_TABLE_NAME][REGISTRY_TABLE_NAME], Item::None) {
31 self.debug("patch section not found");
32 return Ok(());
33 }
34
35 let table = manifest[PATCH_TABLE_NAME][REGISTRY_TABLE_NAME]
36 .as_inline_table()
37 .ok_or_else(|| anyhow!("parsing as table should not failed during 'undo'"))?
38 .clone()
39 .into_table();
40
41 let mut removed_crates = Vec::new();
42
43 for item in &table {
44 let path = item.1["path"]
45 .as_value()
46 .ok_or_else(|| anyhow!("path not found in the patch table"))?
47 .as_str()
48 .ok_or_else(|| anyhow!("path is not a string"))?;
49
50 let path = PathBuf::from(path);
51
52 if path.starts_with(rhack_dir()) {
53 removed_crates.push(item.0);
54 }
55 }
56
57 let mut manifest = load_manifest(&manifest_path)?;
59 let table = manifest[PATCH_TABLE_NAME][REGISTRY_TABLE_NAME]
60 .as_inline_table_mut()
61 .ok_or_else(|| anyhow!("parsing as table should not failed during 'undo'"))?;
62
63 for c in removed_crates {
64 println!("dropped {c:?}");
65 _ = table.remove(c);
66 }
67 if table.is_empty() {
68 _ = manifest.remove_entry(PATCH_TABLE_NAME);
70 }
71
72 match fs::write(&manifest_path, manifest.to_string()) {
73 Ok(()) => (),
74 Err(err) => {
75 return Err(anyhow!(
76 "failed to write to {}: {:#}",
77 &manifest_path.display(),
78 err
79 ))
80 }
81 }
82
83 Ok(())
84 }
85}
86
87impl Undo {
88 fn debug(&self, msg: &str) {
89 if self.verbose {
90 println!("{msg}");
91 }
92 }
93}