Add TOML dependency and new config/startup modules
The TOML dependency is added to `Cargo.lock` to support configuration file parsing. New modules `config` and `startup` are introduced in `doctate-client-core`: - `config`: Handles client configuration loading and saving, including defaults for polling intervals and window hours. - `startup`: Contains routines for initial cleanup tasks, such as removing stale markers and orphan audio files based on defined retention periods.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use time::OffsetDateTime;
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
use time::{OffsetDateTime, UtcOffset};
|
||||
|
||||
/// Current UTC time as an RFC3339 string with second precision, e.g.
|
||||
/// `2026-04-15T10:32:00Z`. Subsecond fractions are deliberately stripped
|
||||
@@ -36,6 +36,27 @@ pub fn filename_stem_to_recorded_at(stem: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an RFC3339 timestamp. Returns `None` on any error so callers
|
||||
/// can fall back to a lenient display path without having to classify
|
||||
/// failures.
|
||||
pub fn parse_rfc3339(s: &str) -> Option<OffsetDateTime> {
|
||||
OffsetDateTime::parse(s, &Rfc3339).ok()
|
||||
}
|
||||
|
||||
/// Extract `HH:MM` in the local timezone from an RFC3339 UTC timestamp.
|
||||
/// On parse failure, returns the raw string truncated to its first 16
|
||||
/// characters — the user still sees something readable even if the
|
||||
/// value is shaped unexpectedly.
|
||||
pub fn extract_hhmm(ts: &str) -> String {
|
||||
match parse_rfc3339(ts) {
|
||||
Some(t) => {
|
||||
let local = t.to_offset(UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC));
|
||||
format!("{:02}:{:02}", local.hour(), local.minute())
|
||||
}
|
||||
None => ts.chars().take(16).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -88,4 +109,35 @@ mod tests {
|
||||
);
|
||||
assert!(now.ends_with('Z'), "expected UTC 'Z' suffix, got {now}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_rfc3339_accepts_utc_z() {
|
||||
let t = parse_rfc3339("2026-04-18T10:32:00Z").expect("parse UTC");
|
||||
assert_eq!(t.hour(), 10);
|
||||
assert_eq!(t.minute(), 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_rfc3339_returns_none_on_garbage() {
|
||||
assert!(parse_rfc3339("not a timestamp").is_none());
|
||||
}
|
||||
|
||||
/// `extract_hhmm` depends on the host timezone for formatting, so we
|
||||
/// can't assert an exact value — but we can assert the shape and
|
||||
/// that the fallback kicks in on unparseable input.
|
||||
#[test]
|
||||
fn extract_hhmm_shape_is_two_colon_two() {
|
||||
let s = extract_hhmm("2026-04-18T10:32:00Z");
|
||||
assert_eq!(s.len(), 5, "expected HH:MM, got {s}");
|
||||
assert!(s.chars().nth(2) == Some(':'), "expected colon at index 2: {s}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_hhmm_falls_back_to_truncated_raw_on_parse_failure() {
|
||||
assert_eq!(extract_hhmm("not-a-timestamp"), "not-a-timestamp");
|
||||
assert_eq!(
|
||||
extract_hhmm("way-longer-than-sixteen-chars"),
|
||||
"way-longer-than-"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user