pub mod adjust;
pub mod begin;
pub mod docs;
pub mod end;
pub mod hold;
pub mod now;
pub mod reflect;
pub mod resume;
pub mod settings;
pub mod setup;
use abscissa_core::{
config::Override, status_warn, tracing::debug, Command, Configurable, FrameworkError, Runnable,
};
use clap::builder::{styling::AnsiColor, Styles};
use human_panic::setup_panic;
use std::path::PathBuf;
use pace_core::{
constants::PACE_CONFIG_FILENAME,
prelude::{get_config_paths, ActivityLogFormatKind, PaceConfig},
};
#[derive(clap::Parser, Command, Debug, Runnable)]
pub enum PaceCmd {
#[clap(visible_alias = "a")]
Adjust(adjust::AdjustCmd),
#[clap(visible_alias = "b")]
Begin(begin::BeginCmd),
#[clap(visible_alias = "e")]
End(end::EndCmd),
#[clap(visible_alias = "h")]
Hold(hold::HoldCmd),
#[clap(visible_alias = "n")]
Now(now::NowCmd),
#[clap(visible_alias = "r")]
Resume(resume::ResumeCmd),
#[clap(visible_alias = "ref")]
Reflect(reflect::ReflectCmd),
Setup(setup::SetupCmd),
#[clap(visible_alias = "s")]
Settings(settings::SettingsCmd),
#[clap(visible_alias = "d")]
Docs(docs::DocsCmd),
}
const fn cli_colour_styles() -> Styles {
Styles::styled()
.header(AnsiColor::BrightBlue.on_default())
.usage(AnsiColor::BrightYellow.on_default())
.literal(AnsiColor::BrightGreen.on_default())
.placeholder(AnsiColor::Magenta.on_default())
}
#[derive(clap::Parser, Command, Debug)]
#[command(name="pace", author, about, styles=cli_colour_styles(), version, arg_required_else_help = true, propagate_version = true, )]
pub struct EntryPoint {
#[command(subcommand)]
cmd: PaceCmd,
#[arg(short, long)]
pub verbose: bool,
#[arg(long, env = "PACE_CONFIG_FILE", value_hint = clap::ValueHint::FilePath)]
pub config: Option<PathBuf>,
#[arg(long, env = "PACE_ACTIVITY_LOG_FILE", value_hint = clap::ValueHint::FilePath)]
pub activity_log_file: Option<PathBuf>,
#[arg(long, env = "PACE_HOME", value_hint = clap::ValueHint::DirPath)]
pub home: Option<PathBuf>,
}
impl Runnable for EntryPoint {
fn run(&self) {
setup_panic!();
self.cmd.run();
}
}
impl Override<PaceConfig> for EntryPoint {
fn override_config(&self, mut config: PaceConfig) -> Result<PaceConfig, FrameworkError> {
if let Some(activity_log_file) = &self.activity_log_file {
debug!("Overriding activity log file with: {:?}", activity_log_file);
match (activity_log_file.parent(), activity_log_file.exists()) {
(Some(dir), false) if dir.exists() => {
std::fs::File::create(activity_log_file)?;
}
(Some(dir), false) if !dir.exists() => {
std::fs::create_dir_all(dir)?;
std::fs::File::create(activity_log_file)?;
}
_ => {}
};
*config.general_mut().activity_log_options_mut().path_mut() =
activity_log_file.to_path_buf();
*config
.general_mut()
.activity_log_options_mut()
.format_kind_mut() = Some(ActivityLogFormatKind::Toml);
};
debug!("Overridden config: {:?}", config);
Ok(config)
}
}
impl Configurable<PaceConfig> for EntryPoint {
fn config_path(&self) -> Option<PathBuf> {
let automatically_determined = get_config_paths(PACE_CONFIG_FILENAME)
.into_iter()
.filter(|f| f.exists())
.collect::<Vec<_>>();
debug!(
"Automatically determined config paths: {:?}",
automatically_determined
);
if automatically_determined.len() > 1 {
status_warn!("Multiple config files found in standard locations, we will use the first one found: {:?}", automatically_determined);
}
let first_automatically_determined = automatically_determined.first();
debug!(
"First automatically determined config path: {:?}",
first_automatically_determined
);
let user_specified = self.config.as_ref().and_then(|f| {
if f.exists() {
Some(f)
} else {
if let Some(parent) = f.parent() {
std::fs::create_dir_all(parent).ok()?;
}
std::fs::File::create(f).ok()?;
Some(f)
}
});
let config_path = match (user_specified, first_automatically_determined) {
(Some(filename), _) => Some(filename.clone()),
(None, Some(first_path)) => Some(first_path.clone()),
_ => None,
};
debug!("Using config path: {:?}", config_path);
config_path
}
fn process_config(&self, config: PaceConfig) -> Result<PaceConfig, FrameworkError> {
let config = self.override_config(config)?;
Ok(config)
}
}