intelli_shell/process/
update.rs

1use crossterm::style::Stylize;
2use tokio_util::sync::CancellationToken;
3
4use crate::{
5    cli::UpdateProcess,
6    config::Config,
7    errors::AppError,
8    format_error,
9    process::{Process, ProcessOutput},
10    service::IntelliShellService,
11    utils::{InstallationMethod, detect_installation_method},
12};
13
14impl Process for UpdateProcess {
15    async fn execute(
16        self,
17        config: Config,
18        service: IntelliShellService,
19        _cancellation_token: CancellationToken,
20    ) -> color_eyre::Result<ProcessOutput> {
21        let current_version_str = env!("CARGO_PKG_VERSION");
22        let current_version_tag = format!("v{current_version_str}");
23        let latest_version = match service.check_new_version().await {
24            Ok(Some(v)) => v,
25            Ok(None) => {
26                return Ok(ProcessOutput::success().stdout(format!(
27                    "You're all set! You are running the latest version of intelli-shell ({}).",
28                    current_version_tag.cyan()
29                )));
30            }
31            Err(AppError::UserFacing(err)) => {
32                return Ok(ProcessOutput::fail().stderr(format_error!(config.theme, "{err}")));
33            }
34            Err(AppError::Unexpected(report)) => return Err(report),
35        };
36        let latest_version_tag = format!("v{latest_version}");
37
38        // Common header for all update-needed messages
39        let header = format!(
40            "šŸš€ A new version is available! ({} -> {})",
41            current_version_tag.yellow(),
42            latest_version_tag.clone().green(),
43        );
44
45        // Detect the installation method to provide tailored instructions
46        match detect_installation_method(&config.data_dir) {
47            // Handle automatic update via the installer
48            InstallationMethod::Installer => {
49                let initial_message = format!("{header}\n\nDownloading ...");
50                println!("{initial_message}");
51
52                let target_version_tag = latest_version_tag.clone();
53                let status = tokio::task::spawn_blocking(move || {
54                    self_update::backends::github::Update::configure()
55                        .repo_owner("lasantosr")
56                        .repo_name("intelli-shell")
57                        .bin_name("intelli-shell")
58                        .show_output(false)
59                        .show_download_progress(true)
60                        .no_confirm(true)
61                        .current_version(current_version_str)
62                        .target_version_tag(&target_version_tag)
63                        .build()?
64                        .update()
65                })
66                .await?;
67
68                println!("\n");
69
70                match status {
71                    Ok(self_update::Status::UpToDate(_)) => unreachable!(),
72                    Ok(self_update::Status::Updated(_)) => Ok(ProcessOutput::success().stdout(format!(
73                        "āœ… Update complete! You are now on intelli-shell {}.\n\nšŸ’” Some updates refine shell \
74                         integration; a terminal restart might be required if you experience any issues.",
75                        latest_version_tag.cyan()
76                    ))),
77                    Err(e) => Ok(ProcessOutput::fail().stderr(format!(
78                        "āŒ Update failed:\n{e}\n\nPlease check your network connection or file permissions.",
79                    ))),
80                }
81            }
82            // Provide clear, copyable instructions for other installation methods
83            installation_method => {
84                let instructions = get_manual_update_instructions(installation_method);
85                let full_message = format!("{header}\n\n{instructions}");
86                Ok(ProcessOutput::success().stdout(full_message))
87            }
88        }
89    }
90}
91
92/// Generates user-friendly update instructions based on the installation method
93fn get_manual_update_instructions(method: InstallationMethod) -> String {
94    match method {
95        InstallationMethod::Cargo => format!(
96            "It looks like you installed with {}. To update, please run:\n\n{}\n",
97            "cargo".yellow(),
98            "  LIBSQLITE3_FLAGS=\"-DSQLITE_ENABLE_MATH_FUNCTIONS\" cargo install intelli-shell --locked".cyan()
99        ),
100        InstallationMethod::Nix => format!(
101            "It looks like you installed with {}. Consider updating it via your Nix configuration.",
102            "Nix".yellow()
103        ),
104        InstallationMethod::Source => format!(
105            "It looks like you installed from {}. You might need to run:\n\n{}\n",
106            "source".yellow(),
107            "  git pull && cargo build --release".cyan()
108        ),
109        InstallationMethod::Unknown(Some(path)) => format!(
110            "Could not determine the installation method. Your executable is located at:\n\n  {}\n\nPlease update \
111             manually or consider reinstalling with the recommended script.",
112            path.cyan()
113        ),
114        InstallationMethod::Unknown(None) => {
115            "Could not determine the installation method. Please update manually.".to_string()
116        }
117        InstallationMethod::Installer => unreachable!(),
118    }
119}