chore: make datetime pure
This commit is contained in:
parent
2ab2b6a52b
commit
e8dc2013bc
2 changed files with 43 additions and 23 deletions
|
|
@ -4,6 +4,7 @@ use std::path::Path;
|
|||
|
||||
use chrono::Datelike;
|
||||
use chrono::NaiveDate;
|
||||
use chrono::Utc;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::config::Settings;
|
||||
|
|
@ -339,7 +340,7 @@ pub fn run(decimal: bool, debug: bool) -> Result<(), StreamdError> {
|
|||
|
||||
// Load all markdown files and extract timesheets
|
||||
let all_shards = load_all_shards(base_folder)?;
|
||||
let timesheets = extract_timesheets(&all_shards)?;
|
||||
let timesheets = extract_timesheets(&all_shards, Utc::now())?;
|
||||
|
||||
// Generate the report
|
||||
let report = generate_report(×heets, ×heet_config)?;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,10 @@ fn shards_to_timesheet_points(shards: &[LocalizedShard]) -> Vec<TimesheetPoint>
|
|||
}
|
||||
|
||||
/// Aggregate timesheet points for a single day into a Timesheet.
|
||||
fn aggregate_timecard_day(points: &[TimesheetPoint]) -> Result<Option<Timesheet>, StreamdError> {
|
||||
fn aggregate_timecard_day(
|
||||
points: &[TimesheetPoint],
|
||||
now: DateTime<Utc>,
|
||||
) -> Result<Option<Timesheet>, StreamdError> {
|
||||
if points.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
|
@ -113,10 +116,15 @@ fn aggregate_timecard_day(points: &[TimesheetPoint]) -> Result<Option<Timesheet>
|
|||
|
||||
// Check that we ended in break mode
|
||||
if !last_is_break {
|
||||
return Err(StreamdError::TimesheetError(format!(
|
||||
"Last Timecard of {} is not a break!",
|
||||
card_date
|
||||
)));
|
||||
if card_date == now.date_naive() {
|
||||
// No closing break yet for today — artificially close at now
|
||||
timecards.push(Timecard::new(last_time, now.time()));
|
||||
} else {
|
||||
return Err(StreamdError::TimesheetError(format!(
|
||||
"Last Timecard of {} is not a break!",
|
||||
card_date
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// Only return a timesheet if there's meaningful data
|
||||
|
|
@ -133,7 +141,10 @@ fn aggregate_timecard_day(points: &[TimesheetPoint]) -> Result<Option<Timesheet>
|
|||
}
|
||||
|
||||
/// Aggregate timesheet points into timesheets, grouped by day.
|
||||
fn aggregate_timecards(points: &[TimesheetPoint]) -> Result<Vec<Timesheet>, StreamdError> {
|
||||
fn aggregate_timecards(
|
||||
points: &[TimesheetPoint],
|
||||
now: DateTime<Utc>,
|
||||
) -> Result<Vec<Timesheet>, StreamdError> {
|
||||
let mut timesheets = Vec::new();
|
||||
|
||||
// Sort points by moment to ensure proper grouping
|
||||
|
|
@ -143,7 +154,7 @@ fn aggregate_timecards(points: &[TimesheetPoint]) -> Result<Vec<Timesheet>, Stre
|
|||
// Group by date
|
||||
for (_date, group) in &sorted_points.iter().chunk_by(|p| p.moment.date_naive()) {
|
||||
let day_points: Vec<_> = group.cloned().collect();
|
||||
if let Some(timesheet) = aggregate_timecard_day(&day_points)? {
|
||||
if let Some(timesheet) = aggregate_timecard_day(&day_points, now)? {
|
||||
timesheets.push(timesheet);
|
||||
}
|
||||
}
|
||||
|
|
@ -152,9 +163,12 @@ fn aggregate_timecards(points: &[TimesheetPoint]) -> Result<Vec<Timesheet>, Stre
|
|||
}
|
||||
|
||||
/// Extract timesheets from localized shards.
|
||||
pub fn extract_timesheets(shards: &[LocalizedShard]) -> Result<Vec<Timesheet>, StreamdError> {
|
||||
pub fn extract_timesheets(
|
||||
shards: &[LocalizedShard],
|
||||
now: DateTime<Utc>,
|
||||
) -> Result<Vec<Timesheet>, StreamdError> {
|
||||
let points = shards_to_timesheet_points(shards);
|
||||
aggregate_timecards(&points)
|
||||
aggregate_timecards(&points, now)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -163,6 +177,11 @@ mod tests {
|
|||
use chrono::{NaiveTime, TimeZone};
|
||||
use indexmap::IndexMap;
|
||||
|
||||
/// A fixed "now" in the past, so tests never match today.
|
||||
fn past_now() -> DateTime<Utc> {
|
||||
Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap()
|
||||
}
|
||||
|
||||
fn point(at: DateTime<Utc>, point_type: TimesheetPointType) -> LocalizedShard {
|
||||
let mut location = IndexMap::new();
|
||||
location.insert(
|
||||
|
|
@ -198,7 +217,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards).unwrap();
|
||||
let result = extract_timesheets(&shards, past_now()).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].date, day.date_naive());
|
||||
|
|
@ -251,7 +270,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards).unwrap();
|
||||
let result = extract_timesheets(&shards, past_now()).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].timecards.len(), 3);
|
||||
|
|
@ -302,7 +321,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards).unwrap();
|
||||
let result = extract_timesheets(&shards, past_now()).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].timecards.len(), 3);
|
||||
|
|
@ -336,7 +355,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards).unwrap();
|
||||
let result = extract_timesheets(&shards, past_now()).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 2);
|
||||
assert_eq!(result[0].date, day1.date_naive());
|
||||
|
|
@ -359,7 +378,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards).unwrap();
|
||||
let result = extract_timesheets(&shards, past_now()).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].special_day_type, Some(SpecialDayType::Vacation));
|
||||
|
|
@ -382,7 +401,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards).unwrap();
|
||||
let result = extract_timesheets(&shards, past_now()).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].special_day_type, Some(SpecialDayType::Holiday));
|
||||
|
|
@ -404,7 +423,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards).unwrap();
|
||||
let result = extract_timesheets(&shards, past_now()).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].special_day_type, Some(SpecialDayType::Undertime));
|
||||
|
|
@ -431,7 +450,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards).unwrap();
|
||||
let result = extract_timesheets(&shards, past_now()).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert!(result[0].is_sick_leave);
|
||||
|
|
@ -454,7 +473,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards).unwrap();
|
||||
let result = extract_timesheets(&shards, past_now()).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert!(result[0].is_sick_leave);
|
||||
|
|
@ -463,7 +482,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_empty_input() {
|
||||
let result = extract_timesheets(&[]).unwrap();
|
||||
let result = extract_timesheets(&[], past_now()).unwrap();
|
||||
assert!(result.is_empty());
|
||||
}
|
||||
|
||||
|
|
@ -483,7 +502,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards);
|
||||
let result = extract_timesheets(&shards, past_now());
|
||||
|
||||
assert!(result.is_err());
|
||||
let err = result.unwrap_err();
|
||||
|
|
@ -511,7 +530,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards);
|
||||
let result = extract_timesheets(&shards, past_now());
|
||||
|
||||
assert!(result.is_err());
|
||||
let err = result.unwrap_err();
|
||||
|
|
@ -534,7 +553,7 @@ mod tests {
|
|||
),
|
||||
];
|
||||
|
||||
let result = extract_timesheets(&shards).unwrap();
|
||||
let result = extract_timesheets(&shards, past_now()).unwrap();
|
||||
|
||||
assert!(result.is_empty());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue