1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
//! `begin` subcommand
use abscissa_core::{status_err, Application, Command, Runnable, Shutdown};
use clap::Parser;
use eyre::Result;
use crate::prelude::PACE_APP;
use pace_core::{
extract_time_or_now, get_storage_from_config, Activity, ActivityKind, ActivityStateManagement,
ActivityStorage, ActivityStore, PaceConfig, SyncStorage,
};
/// `begin` subcommand
#[derive(Command, Debug, Parser)]
pub struct BeginCmd {
/// The Category of the activity you want to start
///
/// You can use the separator you setup in the configuration file
/// to specify a subcategory.
#[clap(short, long)]
category: Option<String>,
/// The time the activity has been started at
#[clap(long)]
time: Option<String>,
/// The description of the activity you want to start
description: String,
/// The tags you want to associate with the activity
#[clap(short, long)]
tags: Option<Vec<String>>,
/// TODO: The project you want to start tracking time for
/// FIXME: involves parsing the project configuration first
#[clap(skip)]
_projects: Option<Vec<String>>,
}
impl Runnable for BeginCmd {
/// Start the application.
fn run(&self) {
if let Err(err) = self.inner_run(&PACE_APP.config()) {
status_err!("{}", err);
PACE_APP.shutdown(Shutdown::Crash);
};
}
}
impl BeginCmd {
/// Inner run implementation for the begin command
pub fn inner_run(&self, config: &PaceConfig) -> Result<()> {
let Self {
category,
time,
description,
..
} = self;
// parse time from string or get now
let date_time = extract_time_or_now(time)?;
// TODO: Parse categories and subcategories from string
// let (category, subcategory) = if let Some(ref category) = category {
// let separator = config.general().category_separator();
// extract_categories(category.as_str(), separator.as_str())
// } else {
// // if no category is given, use the default category
// // FIXME: This should be the default category from the project configuration
// // but for now, we'll just use category defaults
// //
// // FIXME: We might also want to merge the project configuration with the general configuration first to have precedence
// //
// // let category = if let Some(category) = PACE_APP.config().general().default_category() {
// // category
// // } else {
// // &Category::default()
// // };
// (Category::default(), None)
// };
let activity = Activity::builder()
.description(description.clone())
.begin(date_time.into())
.kind(ActivityKind::default())
.category(category.clone())
.build();
let activity_store = ActivityStore::new(get_storage_from_config(config)?);
activity_store.setup_storage()?;
let activity_id = activity_store.begin_activity(activity.clone())?;
if let Some(og_activity_id) = activity.guid() {
if activity_id == *og_activity_id {
activity_store.sync()?;
println!("{activity}");
return Ok(());
}
}
eyre::bail!("Failed to start {activity}");
}
}