1pub mod adjust;
14pub mod begin;
15pub mod docs;
16pub mod end;
17pub mod hold;
18pub mod now;
19pub mod reflect;
20pub mod resume;
21pub mod settings;
22pub mod setup;
23
24use abscissa_core::{
25 config::Override, status_warn, tracing::debug, Command, Configurable, FrameworkError, Runnable,
26};
27use clap::builder::{styling::AnsiColor, Styles};
28use human_panic::setup_panic;
29use std::path::PathBuf;
30
31use pace_core::{
32 constants::PACE_CONFIG_FILENAME,
33 prelude::{get_config_paths, ActivityLogFormatKind, PaceConfig},
34};
35
36#[derive(clap::Parser, Command, Debug, Runnable)]
39pub enum PaceCmd {
40 #[clap(visible_alias = "a")]
42 Adjust(adjust::AdjustCmd),
43
44 #[clap(visible_alias = "b")]
46 Begin(begin::BeginCmd),
47
48 #[clap(visible_alias = "e")]
50 End(end::EndCmd),
51
52 #[clap(visible_alias = "h")]
54 Hold(hold::HoldCmd),
55
56 #[clap(visible_alias = "n")]
58 Now(now::NowCmd),
59
60 #[clap(visible_alias = "r")]
62 Resume(resume::ResumeCmd),
63
64 #[clap(visible_alias = "ref")]
66 Reflect(reflect::ReflectCmd),
67
68 Setup(setup::SetupCmd),
70
71 #[clap(visible_alias = "s")]
73 Settings(settings::SettingsCmd),
74
75 #[clap(visible_alias = "d")]
77 Docs(docs::DocsCmd),
78 }
87
88const fn cli_colour_styles() -> Styles {
90 Styles::styled()
91 .header(AnsiColor::BrightBlue.on_default())
92 .usage(AnsiColor::BrightYellow.on_default())
93 .literal(AnsiColor::BrightGreen.on_default())
94 .placeholder(AnsiColor::Magenta.on_default())
95}
96
97#[derive(clap::Parser, Command, Debug)]
100#[command(name="pace", author, about, styles=cli_colour_styles(), version, arg_required_else_help = true, propagate_version = true, )]
101pub struct EntryPoint {
102 #[command(subcommand)]
103 cmd: PaceCmd,
104
105 #[arg(short, long)]
107 pub verbose: bool,
108
109 #[arg(long, env = "PACE_CONFIG_FILE", value_hint = clap::ValueHint::FilePath)]
111 pub config: Option<PathBuf>,
112
113 #[arg(long, env = "PACE_ACTIVITY_LOG_FILE", value_hint = clap::ValueHint::FilePath)]
115 pub activity_log_file: Option<PathBuf>,
116
117 #[arg(long, env = "PACE_HOME", value_hint = clap::ValueHint::DirPath)]
119 pub home: Option<PathBuf>,
120}
121
122impl Runnable for EntryPoint {
123 fn run(&self) {
124 setup_panic!();
125 self.cmd.run();
126 }
127}
128
129impl Override<PaceConfig> for EntryPoint {
130 fn override_config(&self, mut config: PaceConfig) -> Result<PaceConfig, FrameworkError> {
131 if let Some(activity_log_file) = &self.activity_log_file {
133 debug!("Overriding activity log file with: {:?}", activity_log_file);
134
135 match (activity_log_file.parent(), activity_log_file.exists()) {
137 (Some(dir), false) if dir.exists() => {
138 std::fs::File::create(activity_log_file)?;
139 }
140 (Some(dir), false) if !dir.exists() => {
141 std::fs::create_dir_all(dir)?;
142 std::fs::File::create(activity_log_file)?;
143 }
144 _ => {}
145 };
146
147 *config.general_mut().activity_log_options_mut().path_mut() =
148 activity_log_file.to_path_buf();
149
150 *config
153 .general_mut()
154 .activity_log_options_mut()
155 .format_kind_mut() = Some(ActivityLogFormatKind::Toml);
156 };
157
158 debug!("Overridden config: {:?}", config);
159
160 Ok(config)
161 }
162}
163
164impl Configurable<PaceConfig> for EntryPoint {
166 fn config_path(&self) -> Option<PathBuf> {
168 let automatically_determined = get_config_paths(PACE_CONFIG_FILENAME)
169 .into_iter()
170 .filter(|f| f.exists())
171 .collect::<Vec<_>>();
172
173 debug!(
174 "Automatically determined config paths: {:?}",
175 automatically_determined
176 );
177
178 if automatically_determined.len() > 1 {
179 status_warn!("Multiple config files found in standard locations, we will use the first one found: {:?}", automatically_determined);
180 }
181
182 let first_automatically_determined = automatically_determined.first();
185
186 debug!(
187 "First automatically determined config path: {:?}",
188 first_automatically_determined
189 );
190
191 let user_specified = self.config.as_ref().and_then(|f| {
192 if f.exists() {
193 Some(f)
194 } else {
195 if let Some(parent) = f.parent() {
197 std::fs::create_dir_all(parent).ok()?;
198 }
199
200 std::fs::File::create(f).ok()?;
202 Some(f)
203 }
204 });
205
206 let config_path = match (user_specified, first_automatically_determined) {
210 (Some(filename), _) => Some(filename.clone()),
211 (None, Some(first_path)) => Some(first_path.clone()),
212 _ => None,
213 };
214
215 debug!("Using config path: {:?}", config_path);
216
217 config_path
218 }
219
220 fn process_config(&self, config: PaceConfig) -> Result<PaceConfig, FrameworkError> {
226 let config = self.override_config(config)?;
228
229 Ok(config)
239 }
240}