Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 37 additions & 6 deletions src/cli/arguments.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,57 @@
use super::day_parser::parse_days_of_week;
use crate::cli::week_parser::parse_week_and_part;
use crate::domain::models::{day, line_number::LineNumber};
use clap::{Parser, Subcommand};
use clap::{Parser, Subcommand, ValueEnum};
use color_print::cformat;
use serde::Serialize;
use std::str::FromStr;

#[derive(Parser, ValueEnum, Default, Debug, Clone, Serialize, Copy, PartialEq, Eq)]
pub(crate) enum WeekPart {
#[default]
#[serde(rename = "")]
WHOLE,
A,
B,
}
impl FromStr for WeekPart {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"a" => Ok(WeekPart::A),
"b" => Ok(WeekPart::B),
part => Err(format!("Invalid week part '{part}'")),
}
}
}
#[derive(Parser, Debug)]
pub(crate) struct Week {
/// Week number (defaults to current week if omitted)
#[arg(long = "week", short = 'w')]
pub(crate) number: Option<u8>,

#[arg(long = "week", short = 'w',value_parser = parse_week_and_part )]
pub(crate) week: Option<WeekAndPart>,
/// N:th previous week counted from current week (defaults to 1 if N is omitted)
#[arg(
long = "previous-week",
short,
value_name = "N",
conflicts_with = "number",
conflicts_with = "week",
default_missing_value = Some("1"),
num_args(0..=1),
)]
pub(crate) previous: Option<u8>,

/// Year (defaults to current year if omitted)
#[arg(long, short, requires = "number")]
#[arg(long, short, requires = "week")]
pub(crate) year: Option<i32>,
}

#[derive(Debug, Clone)]
pub(crate) struct WeekAndPart {
pub(crate) number: Option<u8>,
pub(crate) part: Option<WeekPart>,
}

#[derive(Parser, Debug)]
pub(crate) struct Task {
/// Name of the job
Expand Down Expand Up @@ -91,6 +117,10 @@ pub enum Command {
#[arg(long, short, default_value = "table")]
format: Format,

/// Show all rows, including those with no hours reported
#[arg(long)]
full: bool,

#[command(flatten)]
week: Week,
},
Expand Down Expand Up @@ -139,6 +169,7 @@ pub enum Command {
arg_required_else_help = true,
after_help = cformat!("<bold,underline>Examples:</bold,underline>\
\n maconomy get \
\n maconomy get --full\
\n maconomy set 8 --job '<<job name>>' --task '<<task name>>' \
\n maconomy set 8 --job '<<job name>>' --task '<<task name>>' --day 'mon-wed, fri' --week 46 \
\n maconomy set 8 --job '<<job name>>' --task '<<task name>>' --day mo --previous-week 2 \
Expand Down
55 changes: 29 additions & 26 deletions src/cli/commands.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::arguments::Format;
use super::arguments::{Format, WeekAndPart, WeekPart};
use crate::domain::models::day::Days;
use crate::domain::models::line_number::LineNumber;
use crate::domain::models::week::WeekNumber;
Expand Down Expand Up @@ -43,10 +43,10 @@ impl<'a> CommandClient<'a> {
}
}

pub(crate) async fn get_table(&self, week: &WeekNumber) -> anyhow::Result<()> {
pub(crate) async fn get_table(&self, week: &WeekNumber, full: bool) -> anyhow::Result<()> {
let time_sheet = self.repository.lock().await.get_time_sheet(week).await?;

println!("{time_sheet}");
println!("{}", time_sheet.format_table(full));
Ok(())
}

Expand All @@ -59,12 +59,12 @@ impl<'a> CommandClient<'a> {
Ok(())
}

pub(crate) async fn get(&self, week: super::arguments::Week, format: Format) {
let week = get_week_number(&week.number, &week.previous, &week.year);
pub(crate) async fn get(&self, week: super::arguments::Week, format: Format, full: bool) {
let week = get_week_number(week.week, week.year, week.previous);

match format {
Format::Json => self.get_json(&week).await.context("JSON"),
Format::Table => self.get_table(&week).await.context("table"),
Format::Table => self.get_table(&week, full).await.context("table"),
}
.unwrap_or_else(|err| {
exit_with_error!("Failed to get time sheet as {}", error_stack_fmt(&err));
Expand All @@ -82,7 +82,7 @@ impl<'a> CommandClient<'a> {
}

let day = get_days(days.days.clone());
let week = get_week_number(&days.week.number, &days.week.previous, &days.week.year);
let week = get_week_number(days.week.week.clone(),days.week.year, days.week.previous );

self.time_sheet_service
.lock()
Expand All @@ -107,7 +107,7 @@ impl<'a> CommandClient<'a> {
exit_with_error!("`--day` is set but no day was provided");
}

let week = get_week_number(&days.week.number, &days.week.previous, &days.week.year);
let week = get_week_number(days.week.week.clone(), days.week.year, days.week.previous);
self.time_sheet_service
.lock()
.await
Expand All @@ -129,7 +129,7 @@ impl<'a> CommandClient<'a> {
}

pub(crate) async fn delete(&mut self, line_number: &LineNumber, week: super::arguments::Week) {
let week = get_week_number(&week.number, &week.previous, &week.year);
let week = get_week_number(week.week, week.year, week.previous,);

self.repository
.lock()
Expand All @@ -143,7 +143,7 @@ impl<'a> CommandClient<'a> {
}

pub(crate) async fn submit(&mut self, week: super::arguments::Week) {
let week = get_week_number(&week.number, &week.previous, &week.year);
let week = get_week_number(week.week, week.year,week.previous);

self.repository
.lock()
Expand All @@ -157,19 +157,24 @@ impl<'a> CommandClient<'a> {
}

fn get_week_number(
week: &Option<u8>,
previous_week: &Option<u8>,
year: &Option<i32>,
week: Option<WeekAndPart>,
year: Option<i32>,
previous_week: Option<u8>,
) -> WeekNumber {
// NOTE: `week` and `previous_week` are assumed to be mutually exclusive (handled by Clap)
if let Some(week) = previous_week {
nth_previous_week(*week).unwrap_or_else(|err| {
nth_previous_week(week).unwrap_or_else(|err| {
exit_with_error!("{err}");
})
} else {
let week = week.unwrap_or_else(|| WeekNumber::default().number);
WeekNumber::new_with_year_fallback(week, *year)
.unwrap_or_else(|err| exit_with_error!("{err}"))
let y = year.unwrap_or_else(|| chrono::Utc::now().year());
week.map(|week_and_part| {
let number = week_and_part.number.ok_or_else(|| anyhow::anyhow!("Week number is required"))?;
let part = week_and_part.part.unwrap_or(WeekPart::WHOLE);
WeekNumber::new(number, part, y)
})
.unwrap_or_else(|| Ok(WeekNumber::default()))
.unwrap_or_else(|err| {exit_with_error!("{err}");})
}
}

Expand All @@ -183,13 +188,11 @@ fn get_days(days: Option<Days>) -> Days {
}

fn nth_previous_week(n: u8) -> anyhow::Result<WeekNumber> {
let today_week_last = chrono::Local::now().date_naive() - chrono::Duration::weeks(n.into());
let week = today_week_last
.iso_week()
.week()
.try_into()
.expect("Week numbers are always less than 255");
let year = today_week_last.year();

WeekNumber::new(week, year)
let mut week = WeekNumber::of(chrono::Local::now().date_naive());

for _ in 0..n {
week = week.previous();
}

Ok(week)
}
5 changes: 4 additions & 1 deletion src/cli/day_parser.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::domain::models::day::{Day, Days};
use std::convert::TryFrom;
use anyhow::{anyhow, Context};
use nom::{
branch::alt,
Expand Down Expand Up @@ -87,7 +88,9 @@ fn days_in_range((start, end): Range) -> Option<Vec<Day>> {
let (start, end) = (start as u8, end as u8);

if start < end {
Some((start..=end).map(Day::from).collect())
(start..=end)
.map(|d| Day::try_from(d).ok())
.collect()
} else {
None
}
Expand Down
1 change: 1 addition & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub(crate) mod arguments;
pub(crate) mod commands;
pub(crate) mod day_parser;
pub(crate) mod rendering;
mod week_parser;
Loading