#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
mod cli;
mod command;
mod gui;
mod localization;
use ajour_core::fs::CONFIG_DIR;
use ajour_core::utility::{remove_file, rename};
#[cfg(target_os = "linux")]
use anyhow::Context;
use std::env;
use std::path::Path;
#[cfg(target_os = "linux")]
use std::path::PathBuf;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub type Result<T, E = anyhow::Error> = std::result::Result<T, E>;
pub fn main() {
let opts_result = cli::get_opts();
#[cfg(debug_assertions)]
let is_debug = true;
#[cfg(not(debug_assertions))]
let is_debug = false;
let is_cli = opts_result
.as_ref()
.map(|o| &o.command)
.unwrap_or(&None)
.is_some();
let opts = cli::validate_opts_or_exit(opts_result, is_cli, is_debug);
if let Some(data_dir) = &opts.data_directory {
let mut config_dir = CONFIG_DIR.lock().unwrap();
*config_dir = data_dir.clone();
}
setup_logger(is_cli, is_debug).expect("setup logging");
if let Some(cleanup_path) = &opts.self_update_temp {
if let Err(e) = handle_self_update_temp(cleanup_path) {
log_error(&e);
std::process::exit(1);
}
}
log_panics::init();
log::info!("Ajour {} has started.", VERSION);
match opts.command {
Some(command) => {
if let Err(e) = match command {
cli::Command::Backup {
backup_folder,
destination,
flavors,
compression_format,
} => command::backup(backup_folder, destination, flavors, compression_format),
cli::Command::Update => command::update_both(),
cli::Command::UpdateAddons => command::update_all_addons(),
cli::Command::UpdateWeakauras => command::update_all_weakauras(),
cli::Command::Install { url, flavor } => command::install_from_source(url, flavor),
} {
log_error(&e);
}
}
None => {
gui::run(opts);
}
}
}
pub fn log_error(error: &anyhow::Error) {
log::error!("{}", error);
let mut causes = error.chain();
causes.next();
for cause in causes {
log::error!("caused by: {}", cause);
}
}
#[allow(clippy::unnecessary_operation)]
fn setup_logger(is_cli: bool, is_debug: bool) -> Result<()> {
let mut logger = fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{} [{}][{}] {}",
chrono::Local::now().format("%H:%M:%S%.3f"),
record.target(),
record.level(),
message
))
})
.level(log::LevelFilter::Off)
.level_for("panic", log::LevelFilter::Error)
.level_for("ajour", log::LevelFilter::Trace);
if !is_cli {
logger = logger.level_for("ajour_core", log::LevelFilter::Trace);
}
if is_cli || is_debug {
logger = logger.chain(std::io::stdout());
}
if !is_cli && !is_debug {
use std::fs::OpenOptions;
let config_dir = ajour_core::fs::config_dir();
let log_file = OpenOptions::new()
.write(true)
.create(true)
.append(false)
.truncate(true)
.open(config_dir.join("ajour.log"))?;
logger = logger.chain(log_file);
};
logger.apply()?;
Ok(())
}
fn handle_self_update_temp(cleanup_path: &Path) -> Result<()> {
#[cfg(not(target_os = "linux"))]
let current_bin = env::current_exe()?;
#[cfg(target_os = "linux")]
let current_bin =
PathBuf::from(env::var("APPIMAGE").context("error getting APPIMAGE env variable")?);
if current_bin
.file_name()
.unwrap_or_default()
.to_str()
.unwrap_or_default()
.starts_with("tmp_")
{
let main_bin_name = cleanup_path;
let parent_dir = current_bin.parent().unwrap();
let main_bin = parent_dir.join(&main_bin_name);
rename(¤t_bin, &main_bin)?;
} else {
remove_file(cleanup_path)?;
}
log::debug!("Ajour updated successfully");
Ok(())
}