@@ -72,6 +72,24 @@ impl LogCollector {
7272 self . entries . try_read ( ) . ok ( ) . map ( |g| g. clone ( ) )
7373 }
7474
75+ /// Non-blocking count of entries whose timestamp is within the last `window`.
76+ /// Returns `None` if a writer holds the lock.
77+ ///
78+ /// Uses the fact that entries are pushed in time order: scans from the tail
79+ /// and stops at the first entry older than the cutoff. O(k) where k is the
80+ /// number of recent entries (typically small even if the buffer is large).
81+ pub fn try_count_within ( & self , window : std:: time:: Duration ) -> Option < usize > {
82+ let chrono_window = chrono:: Duration :: from_std ( window) . ok ( ) ?;
83+ let cutoff = chrono:: Utc :: now ( ) - chrono_window;
84+ self . entries . try_read ( ) . ok ( ) . map ( |guard| {
85+ guard
86+ . iter ( )
87+ . rev ( )
88+ . take_while ( |e| e. timestamp >= cutoff)
89+ . count ( )
90+ } )
91+ }
92+
7593 /// Get all log entries (blocking version).
7694 pub fn get_all_blocking ( & self ) -> Vec < LogEntry > {
7795 self . entries . blocking_read ( ) . clone ( )
@@ -272,4 +290,32 @@ mod tests {
272290 assert ! ( recent[ 0 ] . detail. contains( "R 7 x1" ) ) ;
273291 assert ! ( recent[ 2 ] . detail. contains( "R 9 x1" ) ) ;
274292 }
293+
294+ #[ tokio:: test]
295+ async fn test_try_count_within_recent_only ( ) {
296+ let collector = LogCollector :: new ( ) ;
297+ let mut old = LogEntry :: new ( Direction :: Rx , FunctionCode :: ReadHoldingRegisters , "old" ) ;
298+ old. timestamp = chrono:: Utc :: now ( ) - chrono:: Duration :: seconds ( 10 ) ;
299+ collector. add ( old) . await ;
300+ for i in 0 ..3 {
301+ collector
302+ . add ( LogEntry :: new (
303+ Direction :: Rx ,
304+ FunctionCode :: ReadHoldingRegisters ,
305+ format ! ( "new {}" , i) ,
306+ ) )
307+ . await ;
308+ }
309+ let count = collector. try_count_within ( std:: time:: Duration :: from_secs ( 1 ) ) ;
310+ assert_eq ! ( count, Some ( 3 ) ) ;
311+ }
312+
313+ #[ tokio:: test]
314+ async fn test_try_count_within_empty ( ) {
315+ let collector = LogCollector :: new ( ) ;
316+ assert_eq ! (
317+ collector. try_count_within( std:: time:: Duration :: from_secs( 1 ) ) ,
318+ Some ( 0 ) ,
319+ ) ;
320+ }
275321}
0 commit comments