diff --git a/espanso/src/cli/env_path.rs b/espanso/src/cli/env_path.rs index c17bb3a..ea18299 100644 --- a/espanso/src/cli/env_path.rs +++ b/espanso/src/cli/env_path.rs @@ -26,7 +26,6 @@ pub fn new() -> CliModule { CliModule { enable_logs: true, disable_logs_terminal_output: true, - requires_paths: true, log_mode: super::LogMode::AppendOnly, subcommand: "env-path".to_string(), entry: env_path_main, @@ -35,7 +34,6 @@ pub fn new() -> CliModule { } fn env_path_main(args: CliModuleArgs) -> i32 { - let paths = args.paths.expect("missing paths argument"); let cli_args = args.cli_args.expect("missing cli_args"); let elevated_priviledge_prompt = cli_args.is_present("prompt"); @@ -43,7 +41,7 @@ fn env_path_main(args: CliModuleArgs) -> i32 { if cli_args.subcommand_matches("register").is_some() { if let Err(error) = crate::path::add_espanso_to_path(elevated_priviledge_prompt) { error_print_and_log(&format!( - "Unable to add 'espanso' command to PATH: {}", + "Unable to add 'espanso' command to PATH: {:?}", error )); return ADD_TO_PATH_FAILURE; @@ -51,7 +49,7 @@ fn env_path_main(args: CliModuleArgs) -> i32 { } else if cli_args.subcommand_matches("unregister").is_some() { if let Err(error) = crate::path::remove_espanso_from_path(elevated_priviledge_prompt) { error_print_and_log(&format!( - "Unable to remove 'espanso' command from PATH: {}", + "Unable to remove 'espanso' command from PATH: {:?}", error )); return ADD_TO_PATH_FAILURE; diff --git a/espanso/src/main.rs b/espanso/src/main.rs index 89f2b24..437ab6b 100644 --- a/espanso/src/main.rs +++ b/espanso/src/main.rs @@ -198,11 +198,11 @@ fn main() { .long("prompt") .required(false) .takes_value(false) - .help("Prompt for permissions if the operation requires elevated privileges."), + .help("macOS only:Prompt for permissions if the operation requires elevated privileges."), ) .subcommand(SubCommand::with_name("register").about("Add 'espanso' command to PATH")) .subcommand(SubCommand::with_name("unregister").about("Remove 'espanso' command from PATH")) - .about("Add or remove the 'espanso' command from the PATH (macOS and Windows only)"), + .about("Add or remove the 'espanso' command from the PATH"), ) // .subcommand(SubCommand::with_name("cmd") // .about("Send a command to the espanso daemon.") @@ -451,7 +451,7 @@ fn main() { } // When started from a Linux app image, override the default handler with the launcher - // to start espanso when double clicked + // to start espanso when launching it directly if std::env::var_os("APPIMAGE").is_some() { handler = CLI_HANDLERS.iter().find(|cli| cli.subcommand == "launcher"); } diff --git a/espanso/src/path/linux.rs b/espanso/src/path/linux.rs index 391f67e..ae450ac 100644 --- a/espanso/src/path/linux.rs +++ b/espanso/src/path/linux.rs @@ -18,18 +18,62 @@ */ use anyhow::Result; +use std::{path::PathBuf}; +use thiserror::Error; pub fn is_espanso_in_path() -> bool { - // Not supported on Linux - true + PathBuf::from("/usr/local/bin/espanso").is_file() } pub fn add_espanso_to_path(_: bool) -> Result<()> { - // Not supported on Linux + let target_link_dir = PathBuf::from("/usr/local/bin"); + let exec_path = get_binary_path()?; + + if !target_link_dir.is_dir() { + return Err(PathError::UsrLocalBinDirDoesNotExist.into()); + } + + let target_link_path = target_link_dir.join("espanso"); + + if let Err(error) = std::os::unix::fs::symlink(&exec_path, &target_link_path) { + return Err(PathError::SymlinkError(error).into()); + } + Ok(()) } pub fn remove_espanso_from_path(_: bool) -> Result<()> { - // Not supported on Linux + let target_link_path = PathBuf::from("/usr/local/bin/espanso"); + + if std::fs::symlink_metadata(&target_link_path).is_err() { + return Err(PathError::SymlinkNotFound.into()); + } + + if let Err(error) = std::fs::remove_file(&target_link_path) { + return Err(PathError::SymlinkError(error).into()); + } + Ok(()) -} \ No newline at end of file +} + +#[derive(Error, Debug)] +pub enum PathError { + #[error("/usr/local/bin directory doesn't exist")] + UsrLocalBinDirDoesNotExist, + + #[error("symlink error: `{0}`")] + SymlinkError(std::io::Error), + + #[error("symlink does not exist, so there is nothing to remove.")] + SymlinkNotFound, +} + +fn get_binary_path() -> Result { + // If executed as part of an AppImage, get the app image path instead of + // the binary itself (which was extracted in a temp directory). + if let Some(app_image_path) = std::env::var_os("APPIMAGE") { + return Ok(PathBuf::from(app_image_path)); + } + + Ok(std::env::current_exe()?) +}