feat(core): implement basic troubleshooting logic
This commit is contained in:
parent
dc6b11cfc8
commit
f42c4ef56e
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -105,6 +105,17 @@ version = "0.1.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
|
@ -508,6 +519,7 @@ dependencies = [
|
|||
"markdown",
|
||||
"named_pipe",
|
||||
"notify",
|
||||
"opener",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -1317,6 +1329,16 @@ dependencies = [
|
|||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opener"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "2.1.1"
|
||||
|
@ -1639,6 +1661,12 @@ dependencies = [
|
|||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.22"
|
||||
|
|
|
@ -55,6 +55,7 @@ dialoguer = "0.8.0"
|
|||
colored = "2.0.0"
|
||||
tempdir = "0.3.7"
|
||||
notify = "4.0.17"
|
||||
opener = "0.5.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
named_pipe = "0.4.1"
|
||||
|
|
|
@ -27,14 +27,22 @@ use espanso_ipc::IPCClient;
|
|||
use espanso_path::Paths;
|
||||
use log::{error, info, warn};
|
||||
|
||||
use crate::{VERSION, exit_code::{
|
||||
DAEMON_ALREADY_RUNNING, DAEMON_GENERAL_ERROR, DAEMON_LEGACY_ALREADY_RUNNING, DAEMON_SUCCESS,
|
||||
WORKER_EXIT_ALL_PROCESSES, WORKER_RESTART, WORKER_SUCCESS,
|
||||
}, ipc::{create_ipc_client_to_worker, IPCEvent}, lock::{acquire_daemon_lock, acquire_legacy_lock, acquire_worker_lock}};
|
||||
use crate::{
|
||||
cli::util::CommandExt,
|
||||
exit_code::{
|
||||
DAEMON_ALREADY_RUNNING, DAEMON_FATAL_CONFIG_ERROR, DAEMON_GENERAL_ERROR,
|
||||
DAEMON_LEGACY_ALREADY_RUNNING, DAEMON_SUCCESS, WORKER_EXIT_ALL_PROCESSES, WORKER_RESTART,
|
||||
WORKER_SUCCESS,
|
||||
},
|
||||
ipc::{create_ipc_client_to_worker, IPCEvent},
|
||||
lock::{acquire_daemon_lock, acquire_legacy_lock, acquire_worker_lock},
|
||||
VERSION,
|
||||
};
|
||||
|
||||
use super::{CliModule, CliModuleArgs};
|
||||
use super::{CliModule, CliModuleArgs, PathsOverrides};
|
||||
|
||||
mod ipc;
|
||||
mod troubleshoot;
|
||||
mod ui;
|
||||
mod watcher;
|
||||
|
||||
|
@ -42,8 +50,6 @@ pub fn new() -> CliModule {
|
|||
#[allow(clippy::needless_update)]
|
||||
CliModule {
|
||||
requires_paths: true,
|
||||
requires_config: true,
|
||||
enable_logs: true,
|
||||
log_mode: super::LogMode::CleanAndAppend,
|
||||
subcommand: "daemon".to_string(),
|
||||
entry: daemon_main,
|
||||
|
@ -53,10 +59,11 @@ pub fn new() -> CliModule {
|
|||
|
||||
fn daemon_main(args: CliModuleArgs) -> i32 {
|
||||
let paths = args.paths.expect("missing paths in daemon main");
|
||||
let config_store = args
|
||||
.config_store
|
||||
.expect("missing config store in worker main");
|
||||
let preferences = crate::preferences::get_default(&paths.runtime).expect("unable to obtain preferences");
|
||||
let paths_overrides = args
|
||||
.paths_overrides
|
||||
.expect("missing paths_overrides in daemon main");
|
||||
let preferences =
|
||||
crate::preferences::get_default(&paths.runtime).expect("unable to obtain preferences");
|
||||
let cli_args = args.cli_args.expect("missing cli_args in daemon main");
|
||||
|
||||
// Make sure only one instance of the daemon is running
|
||||
|
@ -77,6 +84,15 @@ fn daemon_main(args: CliModuleArgs) -> i32 {
|
|||
|
||||
// TODO: we might need to check preconditions: accessibility on macOS, presence of binaries on Linux, etc
|
||||
|
||||
let config_store =
|
||||
match troubleshoot::load_config_or_troubleshoot(&paths, &paths_overrides, false) {
|
||||
Ok((load_result, _)) => load_result.config_store,
|
||||
Err(fatal_err) => {
|
||||
error!("critical error while loading config: {}", fatal_err);
|
||||
return DAEMON_FATAL_CONFIG_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
info!("espanso version: {}", VERSION);
|
||||
// TODO: print os system and version? (with os_info crate)
|
||||
|
||||
|
@ -86,7 +102,7 @@ fn daemon_main(args: CliModuleArgs) -> i32 {
|
|||
|
||||
// TODO: register signals to terminate the worker if the daemon terminates
|
||||
|
||||
spawn_worker(&paths, exit_notify.clone());
|
||||
spawn_worker(&paths, &paths_overrides, exit_notify.clone());
|
||||
|
||||
ipc::initialize_and_spawn(&paths.runtime, exit_notify.clone())
|
||||
.expect("unable to initialize ipc server for daemon");
|
||||
|
@ -135,7 +151,7 @@ fn daemon_main(args: CliModuleArgs) -> i32 {
|
|||
}
|
||||
|
||||
if !has_timed_out {
|
||||
spawn_worker(&paths, exit_notify.clone());
|
||||
spawn_worker(&paths, &paths_overrides, exit_notify.clone());
|
||||
} else {
|
||||
error!("could not restart worker, as the exit process has timed out");
|
||||
}
|
||||
|
@ -150,7 +166,7 @@ fn daemon_main(args: CliModuleArgs) -> i32 {
|
|||
}
|
||||
WORKER_RESTART => {
|
||||
info!("worker requested a restart, spawning a new one...");
|
||||
spawn_worker(&paths, exit_notify.clone());
|
||||
spawn_worker(&paths, &paths_overrides, exit_notify.clone());
|
||||
}
|
||||
_ => {
|
||||
error!("received unexpected exit code from worker {}, exiting", code);
|
||||
|
@ -207,34 +223,33 @@ fn terminate_worker_if_already_running(runtime_dir: &Path) {
|
|||
)
|
||||
}
|
||||
|
||||
fn spawn_worker(paths: &Paths, exit_notify: Sender<i32>) {
|
||||
fn spawn_worker(paths: &Paths, paths_overrides: &PathsOverrides, exit_notify: Sender<i32>) {
|
||||
info!("spawning the worker process...");
|
||||
|
||||
let espanso_exe_path =
|
||||
std::env::current_exe().expect("unable to obtain espanso executable location");
|
||||
|
||||
// Before starting the worker, check if the configuration has any error, and if so, show the troubleshooting GUI
|
||||
let troubleshoot_guard = match troubleshoot::load_config_or_troubleshoot(paths, paths_overrides, true) {
|
||||
Ok((_, troubleshoot_guard)) => troubleshoot_guard,
|
||||
Err(fatal_err) => {
|
||||
error!("critical configuration error detected, unable to restart worker: {:?}", fatal_err);
|
||||
|
||||
// TODO: we should show a "Reload & Retry" button in the troubleshooter to retry
|
||||
// loading the configuration and:
|
||||
// - if the configuration is good -> start the worker
|
||||
// - if the configuration is bad -> show the window again
|
||||
// Until we have this logic, we choose the safest option and kill the daemon
|
||||
// (otherwise, we would have a dangling daemon running after closing the troubleshooting).
|
||||
|
||||
unimplemented!();
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
let mut command = Command::new(&espanso_exe_path.to_string_lossy().to_string());
|
||||
command.args(&["worker", "--monitor-daemon"]);
|
||||
command.env(
|
||||
"ESPANSO_CONFIG_DIR",
|
||||
paths.config.to_string_lossy().to_string(),
|
||||
);
|
||||
command.env(
|
||||
"ESPANSO_PACKAGE_DIR",
|
||||
paths.packages.to_string_lossy().to_string(),
|
||||
);
|
||||
command.env(
|
||||
"ESPANSO_RUNTIME_DIR",
|
||||
paths.runtime.to_string_lossy().to_string(),
|
||||
);
|
||||
|
||||
// TODO: investigate if this is needed here, especially when invoking a form
|
||||
// // On windows, we need to spawn the process as "Detached"
|
||||
// #[cfg(target_os = "windows")]
|
||||
// {
|
||||
// use std::os::windows::process::CommandExt;
|
||||
// //command.creation_flags(0x08000008); // CREATE_NO_WINDOW + DETACHED_PROCESS
|
||||
// }
|
||||
command.with_paths_overrides(paths_overrides);
|
||||
|
||||
let mut child = command.spawn().expect("unable to spawn worker process");
|
||||
|
||||
|
@ -243,6 +258,8 @@ fn spawn_worker(paths: &Paths, exit_notify: Sender<i32>) {
|
|||
std::thread::Builder::new()
|
||||
.name("worker-status-monitor".to_string())
|
||||
.spawn(move || {
|
||||
let _guard = troubleshoot_guard;
|
||||
|
||||
let result = child.wait();
|
||||
if let Ok(status) = result {
|
||||
if let Some(code) = status.code() {
|
||||
|
|
102
espanso/src/cli/daemon/troubleshoot.rs
Normal file
102
espanso/src/cli/daemon/troubleshoot.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::process::{Child, Command};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use espanso_path::Paths;
|
||||
|
||||
use crate::cli::util::CommandExt;
|
||||
use crate::cli::PathsOverrides;
|
||||
use crate::config::ConfigLoadResult;
|
||||
use crate::error_eprintln;
|
||||
use crate::preferences::Preferences;
|
||||
|
||||
pub fn launch_troubleshoot(
|
||||
paths_overrides: &PathsOverrides,
|
||||
) -> Result<TroubleshootGuard> {
|
||||
let espanso_exe_path = std::env::current_exe()?;
|
||||
let mut command = Command::new(&espanso_exe_path.to_string_lossy().to_string());
|
||||
command.args(&["modulo", "troubleshoot"]);
|
||||
command.with_paths_overrides(paths_overrides);
|
||||
|
||||
let child = command.spawn()?;
|
||||
|
||||
Ok(TroubleshootGuard::new(child))
|
||||
}
|
||||
|
||||
pub fn launch_troubleshoot_blocking(paths_overrides: &PathsOverrides) -> Result<()> {
|
||||
let mut guard = launch_troubleshoot(paths_overrides)?;
|
||||
guard.wait()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct TroubleshootGuard {
|
||||
child: Child,
|
||||
}
|
||||
|
||||
impl TroubleshootGuard {
|
||||
pub fn new(child: Child) -> Self {
|
||||
Self { child }
|
||||
}
|
||||
pub fn wait(&mut self) -> Result<()> {
|
||||
self.child.wait()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TroubleshootGuard {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.child.kill();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_config_or_troubleshoot(
|
||||
paths: &Paths,
|
||||
paths_overrides: &PathsOverrides,
|
||||
troubleshoot_non_fatal: bool,
|
||||
) -> Result<(ConfigLoadResult, Option<TroubleshootGuard>)> {
|
||||
match crate::load_config(&paths.config, &paths.packages) {
|
||||
Ok(load_result) => {
|
||||
let mut troubleshoot_handle = None;
|
||||
|
||||
if troubleshoot_non_fatal && !load_result.non_fatal_errors.is_empty() {
|
||||
let preferences =
|
||||
crate::preferences::get_default(&paths.runtime).expect("unable to get preferences");
|
||||
|
||||
if preferences.should_display_troubleshoot_for_non_fatal_errors() {
|
||||
match launch_troubleshoot(paths_overrides) {
|
||||
Ok(handle) => {
|
||||
troubleshoot_handle = Some(handle);
|
||||
},
|
||||
Err(err) => {
|
||||
error_eprintln!("unable to launch troubleshoot GUI: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((load_result, troubleshoot_handle))
|
||||
}
|
||||
Err(fatal_err) => {
|
||||
launch_troubleshoot_blocking(paths_overrides)?;
|
||||
|
||||
bail!("fatal error while loading config: {}", fatal_err);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,10 +17,10 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use log::{error};
|
||||
use self::util::MigrationError;
|
||||
use crate::preferences::Preferences;
|
||||
use crate::exit_code::{LAUNCHER_CONFIG_DIR_POPULATION_FAILURE, LAUNCHER_SUCCESS};
|
||||
use crate::preferences::Preferences;
|
||||
use log::error;
|
||||
|
||||
use super::{CliModule, CliModuleArgs};
|
||||
|
||||
|
@ -49,7 +49,9 @@ fn launcher_main(args: CliModuleArgs) -> i32 {
|
|||
// TODO: should we create a non-gui wizard? We can also use it for the non-modulo versions of espanso
|
||||
|
||||
let paths = args.paths.expect("missing paths in launcher main");
|
||||
let paths_overrides = args.paths_overrides.expect("missing paths overrides in launcher main");
|
||||
let paths_overrides = args
|
||||
.paths_overrides
|
||||
.expect("missing paths overrides in launcher main");
|
||||
let icon_paths = crate::icon::load_icon_paths(&paths.runtime).expect("unable to load icon paths");
|
||||
|
||||
let preferences =
|
||||
|
@ -103,9 +105,8 @@ fn launcher_main(args: CliModuleArgs) -> i32 {
|
|||
} else {
|
||||
false
|
||||
};
|
||||
let is_accessibility_enabled_handler = Box::new(move || {
|
||||
accessibility::is_accessibility_enabled()
|
||||
});
|
||||
let is_accessibility_enabled_handler =
|
||||
Box::new(move || accessibility::is_accessibility_enabled());
|
||||
let enable_accessibility_handler = Box::new(move || {
|
||||
accessibility::prompt_enable_accessibility();
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use espanso_config::{config::ConfigStore, matches::store::MatchStore};
|
||||
use espanso_config::{config::ConfigStore, error::NonFatalErrorSet, matches::store::MatchStore};
|
||||
use espanso_path::Paths;
|
||||
|
||||
pub mod daemon;
|
||||
|
@ -73,6 +73,7 @@ pub struct CliModuleArgs {
|
|||
pub config_store: Option<Box<dyn ConfigStore>>,
|
||||
pub match_store: Option<Box<dyn MatchStore>>,
|
||||
pub is_legacy_config: bool,
|
||||
pub non_fatal_errors: Vec<NonFatalErrorSet>,
|
||||
pub paths: Option<Paths>,
|
||||
pub paths_overrides: Option<PathsOverrides>,
|
||||
pub cli_args: Option<ArgMatches<'static>>,
|
||||
|
@ -84,6 +85,7 @@ impl Default for CliModuleArgs {
|
|||
config_store: None,
|
||||
match_store: None,
|
||||
is_legacy_config: false,
|
||||
non_fatal_errors: Vec::new(),
|
||||
paths: None,
|
||||
paths_overrides: None,
|
||||
cli_args: None,
|
||||
|
|
|
@ -25,6 +25,8 @@ mod form;
|
|||
mod search;
|
||||
#[cfg(feature = "modulo")]
|
||||
mod welcome;
|
||||
#[cfg(feature = "modulo")]
|
||||
mod troubleshoot;
|
||||
|
||||
pub fn new() -> CliModule {
|
||||
#[allow(clippy::needless_update)]
|
||||
|
@ -55,6 +57,10 @@ fn modulo_main(args: CliModuleArgs) -> i32 {
|
|||
return welcome::welcome_main(&paths, &icon_paths);
|
||||
}
|
||||
|
||||
if let Some(_) = cli_args.subcommand_matches("troubleshoot") {
|
||||
return troubleshoot::troubleshoot_main(&paths, &icon_paths);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
|
|
105
espanso/src/cli/modulo/troubleshoot.rs
Normal file
105
espanso/src/cli/modulo/troubleshoot.rs
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2 file: (), errors: () file: (), errors: () 021 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::path::{Path};
|
||||
|
||||
use crate::icon::IconPaths;
|
||||
use crate::preferences::Preferences;
|
||||
use espanso_modulo::troubleshooting::{TroubleshootingHandlers, TroubleshootingOptions};
|
||||
use espanso_path::Paths;
|
||||
|
||||
pub fn troubleshoot_main(paths: &Paths, icon_paths: &IconPaths) -> i32 {
|
||||
let preferences =
|
||||
crate::preferences::get_default(&paths.runtime).expect("unable to initialize preferences");
|
||||
|
||||
let dont_show_again_handler = Box::new(move |dont_show: bool| {
|
||||
preferences.set_should_display_troubleshoot_for_non_fatal_errors(!dont_show);
|
||||
});
|
||||
|
||||
let open_file_handler = Box::new(move |file_path: &Path| {
|
||||
if let Err(err) = opener::open(file_path) {
|
||||
eprintln!("error opening file: {}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let (is_fatal_error, error_sets) = match crate::config::load_config(&paths.config, &paths.packages)
|
||||
{
|
||||
Ok(config_result) => {
|
||||
let error_sets = config_result
|
||||
.non_fatal_errors
|
||||
.into_iter()
|
||||
.map(|error_set| espanso_modulo::troubleshooting::ErrorSet {
|
||||
file: Some(error_set.file),
|
||||
errors: error_set
|
||||
.errors
|
||||
.into_iter()
|
||||
.map(|error| espanso_modulo::troubleshooting::ErrorRecord {
|
||||
level: match error.level {
|
||||
espanso_config::error::ErrorLevel::Error => {
|
||||
espanso_modulo::troubleshooting::ErrorLevel::Error
|
||||
}
|
||||
espanso_config::error::ErrorLevel::Warning => {
|
||||
espanso_modulo::troubleshooting::ErrorLevel::Warning
|
||||
}
|
||||
},
|
||||
message: format!("{:?}", error.error),
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
(false, error_sets)
|
||||
}
|
||||
Err(err) => {
|
||||
let message = format!("{:?}", err);
|
||||
let file_path = if message.contains("default.yml") {
|
||||
let default_file_path = paths.config.join("config").join("default.yml");
|
||||
Some(default_file_path)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
(
|
||||
true,
|
||||
vec![espanso_modulo::troubleshooting::ErrorSet {
|
||||
file: file_path,
|
||||
errors: vec![espanso_modulo::troubleshooting::ErrorRecord {
|
||||
level: espanso_modulo::troubleshooting::ErrorLevel::Error,
|
||||
message: format!("{:?}", err),
|
||||
}],
|
||||
}],
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
espanso_modulo::troubleshooting::show(TroubleshootingOptions {
|
||||
window_icon_path: icon_paths
|
||||
.wizard_icon
|
||||
.as_ref()
|
||||
.map(|path| path.to_string_lossy().to_string()),
|
||||
error_sets,
|
||||
is_fatal_error,
|
||||
handlers: TroubleshootingHandlers {
|
||||
dont_show_again_changed: Some(dont_show_again_handler),
|
||||
open_file: Some(open_file_handler),
|
||||
},
|
||||
}).expect("troubleshoot GUI returned error");
|
||||
|
||||
0
|
||||
}
|
|
@ -17,8 +17,9 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
use anyhow::{Context, Result};
|
||||
use espanso_config::{config::ConfigStore, error::{ErrorLevel, NonFatalErrorSet}, matches::store::MatchStore};
|
||||
use log::{error, info, warn};
|
||||
use std::path::Path;
|
||||
|
||||
const DEFAULT_CONFIG_FILE_CONTENT: &str = include_str!("./res/config/default.yml");
|
||||
|
@ -26,7 +27,10 @@ const DEFAULT_MATCH_FILE_CONTENT: &str = include_str!("./res/config/base.yml");
|
|||
|
||||
pub fn populate_default_config(config_dir: &Path) -> Result<()> {
|
||||
if !config_dir.is_dir() {
|
||||
info!("generating base configuration directory in: {:?}", config_dir);
|
||||
info!(
|
||||
"generating base configuration directory in: {:?}",
|
||||
config_dir
|
||||
);
|
||||
std::fs::create_dir_all(config_dir)?;
|
||||
}
|
||||
|
||||
|
@ -46,13 +50,66 @@ pub fn populate_default_config(config_dir: &Path) -> Result<()> {
|
|||
let match_file = sub_match_dir.join("base.yml");
|
||||
|
||||
if !default_file.is_file() {
|
||||
info!("populating default.yml file with initial content: {:?}", default_file);
|
||||
info!(
|
||||
"populating default.yml file with initial content: {:?}",
|
||||
default_file
|
||||
);
|
||||
std::fs::write(default_file, DEFAULT_CONFIG_FILE_CONTENT)?;
|
||||
}
|
||||
if !match_file.is_file() {
|
||||
info!("populating base.yml file with initial content: {:?}", match_file);
|
||||
info!(
|
||||
"populating base.yml file with initial content: {:?}",
|
||||
match_file
|
||||
);
|
||||
std::fs::write(match_file, DEFAULT_MATCH_FILE_CONTENT)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct ConfigLoadResult {
|
||||
pub config_store: Box<dyn ConfigStore>,
|
||||
pub match_store: Box<dyn MatchStore>,
|
||||
pub is_legacy_config: bool,
|
||||
pub non_fatal_errors: Vec<NonFatalErrorSet>,
|
||||
}
|
||||
|
||||
pub fn load_config(config_path: &Path, packages_path: &Path) -> Result<ConfigLoadResult> {
|
||||
if espanso_config::is_legacy_config(&config_path) {
|
||||
let (config_store, match_store) = espanso_config::load_legacy(&config_path, &packages_path)
|
||||
.context("unable to load legacy config")?;
|
||||
|
||||
Ok(ConfigLoadResult {
|
||||
config_store,
|
||||
match_store,
|
||||
is_legacy_config: true,
|
||||
non_fatal_errors: Vec::new(),
|
||||
})
|
||||
} else {
|
||||
let (config_store, match_store, non_fatal_errors) =
|
||||
espanso_config::load(&config_path).context("unable to load config")?;
|
||||
|
||||
// TODO: add an option to avoid dumping the errors in the logs
|
||||
if !non_fatal_errors.is_empty() {
|
||||
warn!("------- detected some errors in the configuration: -------");
|
||||
for non_fatal_error_set in &non_fatal_errors {
|
||||
warn!(">>> {}", non_fatal_error_set.file.to_string_lossy().to_string());
|
||||
for record in &non_fatal_error_set.errors {
|
||||
if record.level == ErrorLevel::Error {
|
||||
error!("{:?}", record.error);
|
||||
} else {
|
||||
warn!("{:?}", record.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
warn!("-----------------------------------------------------------");
|
||||
}
|
||||
|
||||
Ok(ConfigLoadResult {
|
||||
config_store,
|
||||
match_store,
|
||||
is_legacy_config: false,
|
||||
non_fatal_errors,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ pub const DAEMON_SUCCESS: i32 = 0;
|
|||
pub const DAEMON_ALREADY_RUNNING: i32 = 1;
|
||||
pub const DAEMON_GENERAL_ERROR: i32 = 2;
|
||||
pub const DAEMON_LEGACY_ALREADY_RUNNING: i32 = 3;
|
||||
pub const DAEMON_FATAL_CONFIG_ERROR: i32 = 4;
|
||||
|
||||
pub const MIGRATE_SUCCESS: i32 = 0;
|
||||
pub const MIGRATE_ALREADY_NEW_FORMAT: i32 = 1;
|
||||
|
|
|
@ -33,7 +33,10 @@ use simplelog::{
|
|||
CombinedLogger, ConfigBuilder, LevelFilter, SharedLogger, TermLogger, TerminalMode, WriteLogger,
|
||||
};
|
||||
|
||||
use crate::cli::{LogMode, PathsOverrides};
|
||||
use crate::{
|
||||
cli::{LogMode, PathsOverrides},
|
||||
config::load_config,
|
||||
};
|
||||
|
||||
mod capabilities;
|
||||
mod cli;
|
||||
|
@ -218,6 +221,7 @@ fn main() {
|
|||
.help("Interpret the input data as JSON"),
|
||||
),
|
||||
)
|
||||
.subcommand(SubCommand::with_name("troubleshoot").about("Display the troubleshooting GUI"))
|
||||
.subcommand(SubCommand::with_name("welcome").about("Display the welcome screen")),
|
||||
)
|
||||
// .subcommand(SubCommand::with_name("start")
|
||||
|
@ -441,23 +445,15 @@ fn main() {
|
|||
info!("using runtime dir: {:?}", paths.runtime);
|
||||
|
||||
if handler.requires_config {
|
||||
let (config_store, match_store, is_legacy_config) =
|
||||
if espanso_config::is_legacy_config(&paths.config) {
|
||||
let (config_store, match_store) =
|
||||
espanso_config::load_legacy(&paths.config, &paths.packages)
|
||||
.expect("unable to load legacy config");
|
||||
(config_store, match_store, true)
|
||||
} else {
|
||||
let (config_store, match_store) =
|
||||
espanso_config::load(&paths.config).expect("unable to load config");
|
||||
(config_store, match_store, false)
|
||||
};
|
||||
let config_result =
|
||||
load_config(&paths.config, &paths.packages).expect("unable to load config");
|
||||
|
||||
cli_args.is_legacy_config = is_legacy_config;
|
||||
cli_args.config_store = Some(config_store);
|
||||
cli_args.match_store = Some(match_store);
|
||||
cli_args.is_legacy_config = config_result.is_legacy_config;
|
||||
cli_args.config_store = Some(config_result.config_store);
|
||||
cli_args.match_store = Some(config_result.match_store);
|
||||
cli_args.non_fatal_errors = config_result.non_fatal_errors;
|
||||
|
||||
if is_legacy_config {
|
||||
if config_result.is_legacy_config {
|
||||
warn!("espanso is reading the configuration using compatibility mode, thus some features might not be available");
|
||||
warn!("you can migrate to the new configuration format by running 'espanso migrate' in a terminal");
|
||||
}
|
||||
|
@ -497,7 +493,7 @@ fn get_path_override(matches: &ArgMatches, argument: &str, env_var: &str) -> Opt
|
|||
if path.is_dir() {
|
||||
return Some(path);
|
||||
} else {
|
||||
error!("{} argument was specified, but it doesn't point to a valid directory. Make sure to create it first.", argument);
|
||||
error_eprintln!("{} argument was specified, but it doesn't point to a valid directory. Make sure to create it first.", argument);
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else if let Ok(path) = std::env::var(env_var) {
|
||||
|
@ -505,7 +501,7 @@ fn get_path_override(matches: &ArgMatches, argument: &str, env_var: &str) -> Opt
|
|||
if path.is_dir() {
|
||||
return Some(path);
|
||||
} else {
|
||||
error!("{} env variable was specified, but it doesn't point to a valid directory. Make sure to create it first.", env_var);
|
||||
error_eprintln!("{} env variable was specified, but it doesn't point to a valid directory. Make sure to create it first.", env_var);
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -26,6 +26,8 @@ use super::Preferences;
|
|||
|
||||
const HAS_COMPLETED_WIZARD_KEY: &str = "has_completed_wizard";
|
||||
const SHOULD_DISPLAY_WELCOME_KEY: &str = "should_display_welcome";
|
||||
const SHOULD_DISPLAY_TROUBLESHOOT_FOR_NON_FATAL_ERRORS: &str =
|
||||
"should_display_troubleshoot_for_non_fatal_errors";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DefaultPreferences<KVSType: KVS> {
|
||||
|
@ -69,4 +71,12 @@ impl<KVSType: KVS> Preferences for DefaultPreferences<KVSType> {
|
|||
fn set_should_display_welcome(&self, value: bool) {
|
||||
self.set(SHOULD_DISPLAY_WELCOME_KEY, value);
|
||||
}
|
||||
|
||||
fn should_display_troubleshoot_for_non_fatal_errors(&self) -> bool {
|
||||
self.get(SHOULD_DISPLAY_TROUBLESHOOT_FOR_NON_FATAL_ERRORS).unwrap_or(true)
|
||||
}
|
||||
|
||||
fn set_should_display_troubleshoot_for_non_fatal_errors(&self, value: bool) {
|
||||
self.set(SHOULD_DISPLAY_TROUBLESHOOT_FOR_NON_FATAL_ERRORS, value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ pub trait Preferences: Send + Sync + Clone {
|
|||
|
||||
fn should_display_welcome(&self) -> bool;
|
||||
fn set_should_display_welcome(&self, value: bool);
|
||||
|
||||
fn should_display_troubleshoot_for_non_fatal_errors(&self) -> bool;
|
||||
fn set_should_display_troubleshoot_for_non_fatal_errors(&self, value: bool);
|
||||
}
|
||||
|
||||
pub fn get_default(runtime_dir: &Path) -> Result<impl Preferences> {
|
||||
|
|
Loading…
Reference in New Issue
Block a user