cargo_rhack/cmd/
undo.rs

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/// Undo everything
11#[derive(Parser, Debug)]
12pub struct Undo {
13    /// Verbose output.
14    #[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        // NOTE: To avoid being mutable borrow even though borrowed as immutable once.
58        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            // we probably can remove the whole table if it's empty
69            _ = 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}