Compare commits

10 Commits

Author SHA1 Message Date
af3c964c8e chore(deps): update rust crate tokio to v1.45.1
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
2025-05-25 00:14:47 +00:00
e1f6477fac fix(deps): update rust crate uuid to v1.17.0
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-05-23 03:15:00 +00:00
2ba53ca505 chore(deps): update rust crate clap to v4.5.38
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-05-11 03:14:54 +00:00
f739ba4a36 fix(deps): update all dependencies
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-05-07 00:15:33 +00:00
2afa9448b3 chore(deps): update all dependencies
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-04-14 03:15:03 +00:00
bc310d4b72 chore(deps): update rust crate tokio to v1.44.2
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-06 00:14:09 +00:00
c63b325a76 chore(deps): update rust crate clap to v4.5.35
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-04-02 00:15:46 +00:00
2ad6401655 chore(deps): update rust crate clap to v4.5.34
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-03-27 01:57:16 +00:00
01e620cde4 chore(deps): update rust crate clap to v4.5.33
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-03-26 19:56:39 +00:00
fb7d4a028e feat: add current hours
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-26 16:08:50 +01:00
2 changed files with 100 additions and 46 deletions

28
Cargo.lock generated
View File

@@ -84,9 +84,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.97" version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
@@ -156,9 +156,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.40" version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [ dependencies = [
"android-tzdata", "android-tzdata",
"iana-time-zone", "iana-time-zone",
@@ -171,9 +171,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.32" version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -181,9 +181,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.32" version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -211,7 +211,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]] [[package]]
name = "clock" name = "clock"
version = "0.1.0" version = "0.0.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@@ -784,9 +784,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.44.1" version = "1.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@@ -895,11 +895,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.16.0" version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
dependencies = [ dependencies = [
"getrandom 0.3.2", "getrandom 0.3.2",
"js-sys",
"wasm-bindgen",
] ]
[[package]] [[package]]

View File

@@ -61,28 +61,31 @@ async fn main() -> anyhow::Result<()> {
match cli.command.expect("to have a command available") { match cli.command.expect("to have a command available") {
Commands::List { limit, project } => { Commands::List { limit, project } => {
let mut timetable = timetable.clone();
let days = &timetable let days = &timetable
.days .days
.iter() .iter_mut()
.filter(|d| { .map(|d| {
if let Some(project) = &project { if let Some(project) = &project {
Some(project) == d.project.as_ref() d.entry = d
.entry
.iter()
.filter(|d| d.project.as_ref() == Some(project))
.cloned()
.collect::<Vec<_>>();
d
} else { } else {
true d
} }
}) })
.filter(|d| !d.entry.is_empty())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let days = days.iter().rev().take(limit).collect::<Vec<_>>(); let days = days.iter().rev().take(limit).collect::<Vec<_>>();
for day in days { for day in days {
println!( println!(
"{}{}{}\n {}{}\n", "{}{}\n{}\n",
day.clock_in.with_timezone(&Local {}).format("%Y-%m-%d"), day.date.format("%Y-%m-%d"),
if let Some(project) = &day.project {
format!(" project: {}", project)
} else {
"".into()
},
if day.breaks.is_empty() { if day.breaks.is_empty() {
"".into() "".into()
} else { } else {
@@ -91,28 +94,64 @@ async fn main() -> anyhow::Result<()> {
day.breaks.iter().fold(0, |acc, _| acc + 30) day.breaks.iter().fold(0, |acc, _| acc + 30)
) )
}, },
day.clock_in.with_timezone(&Local {}).format("%H:%M"), day.entry
if let Some(clockout) = &day.clock_out { .iter()
format!(" - {}", clockout.with_timezone(&Local {}).format("%H:%M")) .map(|e| {
format!(
" - {} - {}{}",
e.clock_in.with_timezone(&Local {}).format("%H:%M"),
if let Some(clockout) = &e.clock_out {
clockout
.with_timezone(&Local {})
.format("%H:%M")
.to_string()
} else if day.date == now.date_naive() {
let working_hours = e.clock_in - now;
format!(
"unclosed, current hours: {}h{}m",
working_hours.num_hours().abs(),
working_hours.num_minutes().abs() % 60
)
} else { } else {
" - unclosed".into() "unclosed".into()
}, },
if let Some(project) = &e.project {
format!(": project: {}", project)
} else {
"".into()
}
)
})
.collect::<Vec<_>>()
.join("\n"),
) )
} }
} }
Commands::In { project } => { Commands::In { project } => {
timetable.days.push(Day { match timetable.get_day(now) {
Some(d) => {
d.entry.push(ClockIn {
clock_in: now, clock_in: now,
clock_out: None, clock_out: None,
breaks: Vec::default(),
project, project,
}); });
} }
Commands::Out { project } => match timetable.get_day(project, now) { None => timetable.days.push(Day {
entry: vec![ClockIn {
clock_in: now,
clock_out: None,
project,
}],
breaks: Vec::default(),
date: now.date_naive(),
}),
};
}
Commands::Out { project } => match timetable.get_day_entry(project, now) {
Some(day) => day.clock_out = Some(now), Some(day) => day.clock_out = Some(now),
None => todo!(), None => todo!(),
}, },
Commands::Break { project } => match timetable.get_day(project, now) { Commands::Break { project } => match timetable.get_day(now) {
Some(day) => day.breaks.push(Break {}), Some(day) => day.breaks.push(Break {}),
None => todo!(), None => todo!(),
}, },
@@ -120,6 +159,7 @@ async fn main() -> anyhow::Result<()> {
let to_resolve = timetable let to_resolve = timetable
.days .days
.iter_mut() .iter_mut()
.flat_map(|d| &mut d.entry)
.filter(|d| d.clock_out.is_none()) .filter(|d| d.clock_out.is_none())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -204,15 +244,18 @@ fn parse_string_to_time(v: &str) -> anyhow::Result<chrono::NaiveTime> {
}) })
.context("failed to parse int to hour") .context("failed to parse int to hour")
} }
#[derive(Clone, Debug, Serialize, Deserialize)]
struct ClockIn {
clock_in: chrono::DateTime<chrono::Utc>,
clock_out: Option<chrono::DateTime<chrono::Utc>>,
project: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
struct Day { struct Day {
clock_in: chrono::DateTime<chrono::Utc>, date: chrono::NaiveDate,
clock_out: Option<chrono::DateTime<chrono::Utc>>, entry: Vec<ClockIn>,
breaks: Vec<Break>, breaks: Vec<Break>,
project: Option<String>,
} }
#[derive(Default, Clone, Debug, Serialize, Deserialize)] #[derive(Default, Clone, Debug, Serialize, Deserialize)]
@@ -224,12 +267,21 @@ struct TimeTable {
} }
impl TimeTable { impl TimeTable {
pub fn get_day( pub fn get_day(&mut self, now: chrono::DateTime<chrono::Utc>) -> Option<&mut Day> {
let item = self
.days
.iter_mut()
.find(|d| d.date.format("%Y-%m-%d").to_string() == now.format("%Y-%m-%d").to_string());
item
}
pub fn get_day_entry(
&mut self, &mut self,
project: Option<String>, project: Option<String>,
now: chrono::DateTime<chrono::Utc>, now: chrono::DateTime<chrono::Utc>,
) -> Option<&mut Day> { ) -> Option<&mut ClockIn> {
let item = self.days.iter_mut().find(|d| { let item = self.days.iter_mut().flat_map(|d| &mut d.entry).find(|d| {
if d.project != project { if d.project != project {
return false; return false;
} }