use chrono::{FixedOffset, NaiveTime};
use chrono_tz::Tz;
#[cfg(feature = "clap")]
use clap::Parser;
use getset::Getters;
use pace_time::{date_time::PaceDateTime, time_zone::PaceTimeZoneKind, Validate};
use tracing::debug;
use typed_builder::TypedBuilder;
use crate::{
config::PaceConfig,
domain::intermission::IntermissionAction,
error::{PaceResult, UserMessage},
service::activity_store::ActivityStore,
storage::{get_storage_from_config, ActivityStateManagement, SyncStorage},
};
#[derive(Debug)]
#[cfg_attr(feature = "clap", derive(Parser))]
#[cfg_attr(
feature = "clap", clap(group = clap::ArgGroup::new("tz").multiple(false).required(false)))]
pub struct HoldCommandOptions {
#[cfg_attr(
feature = "clap",
clap(long, value_name = "Pause Time", visible_alias = "at")
)]
pause_at: Option<NaiveTime>,
#[cfg_attr(feature = "clap", clap(short, long, value_name = "Reason"))]
reason: Option<String>,
#[cfg_attr(feature = "clap", clap(long, visible_alias = "new"))]
new_if_exists: bool,
#[cfg_attr(
feature = "clap",
clap(
short = 'z',
long,
value_name = "Time Zone",
group = "tz",
visible_alias = "tz"
)
)]
time_zone: Option<Tz>,
#[cfg_attr(
feature = "clap",
clap(
short = 'Z',
long,
value_name = "Time Zone Offset",
group = "tz",
visible_alias = "tzo"
)
)]
time_zone_offset: Option<FixedOffset>,
}
impl HoldCommandOptions {
#[tracing::instrument(skip(self))]
pub fn handle_hold(&self, config: &PaceConfig) -> PaceResult<UserMessage> {
let Self {
pause_at,
reason,
new_if_exists,
time_zone,
time_zone_offset,
} = self;
let date_time = PaceDateTime::try_from((
pause_at.as_ref(),
PaceTimeZoneKind::try_from((time_zone.as_ref(), time_zone_offset.as_ref()))?,
PaceTimeZoneKind::from(config.general().default_time_zone().as_ref()),
))?
.validate()?;
debug!("Parsed date time: {date_time:?}");
let action = IntermissionAction::from(*new_if_exists);
debug!("Intermission action: {action}");
let hold_opts = HoldOptions::builder()
.action(action)
.reason(reason.clone())
.begin_time(date_time)
.build();
debug!("Hold options: {hold_opts:?}");
let activity_store = ActivityStore::with_storage(get_storage_from_config(config)?)?;
let user_message =
if let Some(activity) = activity_store.hold_most_recent_active_activity(hold_opts)? {
debug!("Held {}", activity.activity());
activity_store.sync()?;
format!("Held {}", activity.activity())
} else {
"No unfinished activities to hold.".to_string()
};
Ok(UserMessage::new(user_message))
}
}
#[derive(Debug, Clone, PartialEq, TypedBuilder, Eq, Hash, Default, Getters)]
#[getset(get = "pub")]
#[non_exhaustive]
pub struct HoldOptions {
#[builder(default)]
action: IntermissionAction,
#[builder(default, setter(into))]
begin_time: PaceDateTime,
#[builder(default, setter(into))]
reason: Option<String>,
}