intelli_shell/process/
update.rs

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