arm_toolchain/cli/
remove.rs

1use futures::future::try_join_all;
2use humansize::DECIMAL;
3use indicatif::{MultiProgress, ProgressBar};
4use tokio_util::sync::CancellationToken;
5
6use crate::{
7    cli::{CliError, PROGRESS_STYLE_DELETE, PROGRESS_STYLE_DELETE_SPINNER, ctrl_c_cancel, msg},
8    toolchain::{RemoveProgress, ToolchainClient, ToolchainError, ToolchainVersion},
9};
10
11/// Configuration for [`remove`].
12#[derive(Debug, clap::Parser)]
13pub struct RemoveArgs {
14    /// Version of toolchain to remove, or "all"
15    pub version: ToolchainVersion,
16}
17
18/// Remove a toolchain from the system.
19pub async fn remove(args: RemoveArgs) -> Result<(), CliError> {
20    let client = ToolchainClient::using_data_dir().await?;
21    let toolchains = client.installed_versions().await?;
22
23    if args.version.name == "all" {
24        let old_active = client.active_toolchain();
25        client.set_active_toolchain(None).await?;
26
27        if toolchains.is_empty() && old_active.is_none() {
28            return Err(CliError::NoToolchainEnabled);
29        }
30
31        let cancel_token = ctrl_c_cancel();
32        let multi_progress = MultiProgress::new();
33        let mut futs = vec![];
34
35        for version in toolchains {
36            let client = client.clone();
37            let tok = cancel_token.clone();
38            let multi_progress = multi_progress.clone();
39
40            futs.push(remove_with_progress_bar(
41                client,
42                version,
43                tok,
44                multi_progress,
45            ));
46        }
47
48        let out = try_join_all(futs).await?;
49        let total_bytes = out.iter().sum::<u64>();
50
51        println!(
52            "Removed {} toolchains ({})",
53            out.len(),
54            humansize::format_size(total_bytes, DECIMAL),
55        );
56
57        cancel_token.cancel();
58    } else {
59        if !toolchains.contains(&args.version) {
60            return Err(CliError::CannotRemoveMissingToolchain {
61                version: args.version,
62            });
63        }
64
65        let cancel_token = ctrl_c_cancel();
66        let multi = MultiProgress::new();
67        let bytes =
68            remove_with_progress_bar(client, args.version.clone(), cancel_token.clone(), multi)
69                .await?;
70
71        cancel_token.cancel();
72
73        msg!(
74            "Removed",
75            "{} ({})",
76            args.version,
77            humansize::format_size(bytes, DECIMAL),
78        );
79    }
80
81    Ok(())
82}
83
84async fn remove_with_progress_bar(
85    client: ToolchainClient,
86    version: ToolchainVersion,
87    cancel_token: CancellationToken,
88    multi_progress: MultiProgress,
89) -> Result<u64, ToolchainError> {
90    let bar = ProgressBar::no_length()
91        .with_style(PROGRESS_STYLE_DELETE_SPINNER.clone())
92        .with_message(format!("Removing {version}"));
93    multi_progress.add(bar.clone());
94
95    let progress = |status| match status {
96        RemoveProgress::Start { total_bytes } => {
97            bar.reset();
98            bar.set_length(total_bytes);
99            bar.set_style(PROGRESS_STYLE_DELETE.clone());
100        }
101        RemoveProgress::Progress { bytes_removed } => {
102            bar.set_position(bytes_removed);
103        }
104        RemoveProgress::End => {
105            bar.finish_with_message(format!("{version} is removed"));
106        }
107    };
108
109    client.remove(&version, progress, &cancel_token).await?;
110
111    Ok(bar.length().unwrap_or(0))
112}