1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! Startup recovery: list day files, and find the seq high-water mark.
//!
//! Why: `HarnessEvent.seq` is console-assigned and must never repeat across a
//! restart (DOC-73 §4.3 — "seq is now minted once, by console"). The only
//! source of truth for "what was the last seq console handed out" is the log
//! itself, since the in-memory ring is empty on every fresh process. This
//! module is what [`super::DurableLog::open`] calls before it starts accepting
//! new events.
//! What: [`list_log_files`] enumerates day files in date order, ignoring
//! anything whose name is not a `day_file_name` (a stray file a human or
//! another tool dropped in the directory must not abort startup).
//! [`recover_next_seq`] scans files newest-first and returns the highest seq
//! found plus one, tolerating a truncated final line via
//! [`super::format::read_events`]; an empty log starts at 1. [`earliest_seq`]
//! is the first event's seq in the OLDEST retained file — the boundary
//! [`super::replay`] compares `since_seq` against to decide whether a replay
//! request predates everything retention still holds.
//! Test: `super::tests::recover_next_seq_starts_at_one_with_no_files`,
//! `super::tests::recover_next_seq_continues_from_the_newest_non_empty_file`,
//! `super::tests::recover_next_seq_tolerates_a_truncated_final_line`,
//! `super::tests::earliest_seq_reads_the_oldest_retained_file`.
use ;
use NaiveDate;
use parse_day_file_name;
use LogError;
use read_events;
/// One day file, its date parsed out of the filename, and its full path.
pub type DayFile = ;
/// List every day file in `dir`, sorted ascending by date (oldest first).
///
/// A missing directory is not an error — it returns an empty list, so a
/// first-ever run (before [`trusty_common::uds::prepare_socket_dir`] has
/// created anything) recovers cleanly at seq 1 rather than failing.
///
/// Test: `super::tests::list_log_files_ignores_non_matching_names`,
/// `super::tests::list_log_files_sorts_ascending_by_date`.
pub async
/// The seq a fresh [`super::DurableLog`] should assign next: one past the
/// highest seq recorded anywhere in `files`, or `1` if none is found.
///
/// Scans newest-first and stops at the first file that yields at least one
/// event — under normal operation that is always the very last file, but
/// scanning backward instead of assuming it tolerates a day boundary that
/// rolled over onto an empty file right before a crash (`super::tests::
/// recover_next_seq_continues_from_the_newest_non_empty_file` covers exactly
/// this).
///
/// Test: `super::tests::recover_next_seq_starts_at_one_with_no_files`,
/// `super::tests::recover_next_seq_continues_from_the_newest_non_empty_file`,
/// `super::tests::recover_next_seq_tolerates_a_truncated_final_line`.
pub async
/// The seq of the first event in the OLDEST retained file — `None` when no
/// file holds a single readable event (a brand-new log, or every retained
/// file is empty/fully truncated).
///
/// This is the boundary [`super::replay::replay_since`] compares an incoming
/// `since_seq` against: a request older than this value asks for history
/// retention has already discarded, and must get an explicit gap marker
/// rather than silently starting from whatever remains.
///
/// Test: `super::tests::earliest_seq_reads_the_oldest_retained_file`,
/// `super::tests::earliest_seq_is_none_for_an_empty_log`.
pub async