@@ -69,15 +69,15 @@ use aimdb_core::buffer::{BufferCounters, BufferMetrics, BufferMetricsSnapshot};
6969/// # Example
7070/// ```no_run
7171/// use aimdb_embassy_adapter::EmbassyBuffer;
72- /// use aimdb_core::buffer::{BufferBackend, BufferCfg };
72+ /// use aimdb_core::buffer::{Buffer, BufferReader };
7373///
7474/// // Create an SPMC ring buffer with capacity 32, 4 subscribers, 2 publishers
7575/// type MyBuffer = EmbassyBuffer<u32, 32, 4, 2, 4>;
76- /// static BUFFER: MyBuffer = MyBuffer::new_spmc();
7776///
7877/// # async fn example() {
79- /// let mut reader = BUFFER.subscribe();
80- /// BUFFER.push(42);
78+ /// let buffer: MyBuffer = MyBuffer::new_spmc();
79+ /// let mut reader = buffer.subscribe();
80+ /// buffer.push(42);
8181/// let value = reader.recv().await.unwrap();
8282/// # }
8383/// ```
@@ -240,6 +240,20 @@ impl<
240240 self
241241 }
242242
243+ fn peek ( & self ) -> Option < T > {
244+ match & * self . inner {
245+ // Watch stores the latest value natively; try_get() clones it
246+ // without consuming a receiver slot or advancing any cursor.
247+ EmbassyBufferInner :: Watch ( watch) => watch. try_get ( ) ,
248+ // Channel<_, T, 1>::try_peek() clones the pending slot without
249+ // removing it (the slot is drained once a consumer receives).
250+ // Mirrors the Tokio Mailbox arm.
251+ EmbassyBufferInner :: Mailbox ( channel) => channel. try_peek ( ) . ok ( ) ,
252+ // PubSub has no canonical latest — see design 031 §SPMC Ring.
253+ EmbassyBufferInner :: SpmcRing ( _) => None ,
254+ }
255+ }
256+
243257 #[ cfg( feature = "metrics" ) ]
244258 fn metrics_snapshot ( & self ) -> Option < BufferMetricsSnapshot > {
245259 Some ( <Self as BufferMetrics >:: metrics ( self ) )
@@ -546,6 +560,40 @@ impl<
546560mod tests {
547561 use super :: * ;
548562
563+ // ── Host-test scaffolding ────────────────────────────────────────────
564+ // The crate links `defmt` (workspace dep) and embassy-time's
565+ // `defmt-timestamp-uptime`, but on the host neither a defmt logger nor a
566+ // time driver exists. Provide no-op stubs so the test binary links. Run via
567+ // the `test` Make target, or directly:
568+ // cargo test -p aimdb-embassy-adapter \
569+ // --no-default-features --features "alloc,embassy-sync,embassy-time"
570+ // (`embassy-runtime` would pull the cortex-m executor, which can't host-build.)
571+ #[ defmt:: global_logger]
572+ struct TestLogger ;
573+
574+ unsafe impl defmt:: Logger for TestLogger {
575+ fn acquire ( ) { }
576+ unsafe fn flush ( ) { }
577+ unsafe fn release ( ) { }
578+ unsafe fn write ( _bytes : & [ u8 ] ) { }
579+ }
580+
581+ #[ defmt:: panic_handler]
582+ fn defmt_panic ( ) -> ! {
583+ core:: panic!( "defmt panic in host test" )
584+ }
585+
586+ // Trivial time driver so `_embassy_time_now` resolves on the host. peek()
587+ // never reads the clock; the driver only needs to exist for linking.
588+ struct TestTimeDriver ;
589+ impl embassy_time_driver:: Driver for TestTimeDriver {
590+ fn now ( & self ) -> u64 {
591+ 0
592+ }
593+ fn schedule_wake ( & self , _at : u64 , _waker : & core:: task:: Waker ) { }
594+ }
595+ embassy_time_driver:: time_driver_impl!( static TEST_TIME_DRIVER : TestTimeDriver = TestTimeDriver ) ;
596+
549597 // Note: Embassy tests typically run on actual embedded targets or with embassy-executor
550598 // For now, these are basic compilation tests. Integration tests would need embassy-executor.
551599
@@ -571,4 +619,75 @@ mod tests {
571619 let cfg3 = BufferCfg :: Mailbox ;
572620 let _buf3: TestBuffer = Buffer :: new ( & cfg3) ;
573621 }
622+
623+ // ========================================================================
624+ // peek() Tests — non-destructive buffer-native reads (design 031)
625+ //
626+ // push()/peek() are synchronous and lock a CriticalSectionRawMutex; the
627+ // `critical-section` std impl in dev-dependencies provides the host-side
628+ // implementation, so these run without an embassy executor. Run with:
629+ // cargo test -p aimdb-embassy-adapter \
630+ // --no-default-features --features "alloc,embassy-sync,embassy-time"
631+ // ========================================================================
632+
633+ use aimdb_core:: buffer:: DynBuffer ;
634+
635+ type PeekBuffer = EmbassyBuffer < i32 , 8 , 4 , 2 , 4 > ;
636+
637+ #[ test]
638+ fn test_peek_single_latest_empty ( ) {
639+ let buffer: PeekBuffer = PeekBuffer :: new_watch ( ) ;
640+ assert_eq ! ( DynBuffer :: peek( & buffer) , None ) ;
641+ }
642+
643+ #[ test]
644+ fn test_peek_single_latest_returns_latest ( ) {
645+ let buffer: PeekBuffer = PeekBuffer :: new_watch ( ) ;
646+ DynBuffer :: push ( & buffer, 1 ) ;
647+ DynBuffer :: push ( & buffer, 2 ) ;
648+ DynBuffer :: push ( & buffer, 3 ) ;
649+ assert_eq ! ( DynBuffer :: peek( & buffer) , Some ( 3 ) ) ;
650+ }
651+
652+ #[ test]
653+ fn test_peek_single_latest_is_non_destructive ( ) {
654+ let buffer: PeekBuffer = PeekBuffer :: new_watch ( ) ;
655+ DynBuffer :: push ( & buffer, 42 ) ;
656+ // Multiple peeks return the same value.
657+ assert_eq ! ( DynBuffer :: peek( & buffer) , Some ( 42 ) ) ;
658+ assert_eq ! ( DynBuffer :: peek( & buffer) , Some ( 42 ) ) ;
659+ }
660+
661+ #[ test]
662+ fn test_peek_mailbox_empty ( ) {
663+ let buffer: PeekBuffer = PeekBuffer :: new_mailbox ( ) ;
664+ assert_eq ! ( DynBuffer :: peek( & buffer) , None ) ;
665+ }
666+
667+ #[ test]
668+ fn test_peek_mailbox_returns_pending ( ) {
669+ let buffer: PeekBuffer = PeekBuffer :: new_mailbox ( ) ;
670+ DynBuffer :: push ( & buffer, 7 ) ;
671+ assert_eq ! ( DynBuffer :: peek( & buffer) , Some ( 7 ) ) ;
672+ // Peek is non-destructive: the slot is still occupied.
673+ assert_eq ! ( DynBuffer :: peek( & buffer) , Some ( 7 ) ) ;
674+ }
675+
676+ #[ test]
677+ fn test_peek_mailbox_reflects_overwrite ( ) {
678+ let buffer: PeekBuffer = PeekBuffer :: new_mailbox ( ) ;
679+ DynBuffer :: push ( & buffer, 1 ) ;
680+ DynBuffer :: push ( & buffer, 2 ) ;
681+ assert_eq ! ( DynBuffer :: peek( & buffer) , Some ( 2 ) ) ;
682+ }
683+
684+ #[ test]
685+ fn test_peek_spmc_ring_returns_none ( ) {
686+ // PubSub has no canonical latest — see design 031 §SPMC Ring.
687+ let buffer: PeekBuffer = PeekBuffer :: new_spmc ( ) ;
688+ assert_eq ! ( DynBuffer :: peek( & buffer) , None ) ;
689+ DynBuffer :: push ( & buffer, 1 ) ;
690+ DynBuffer :: push ( & buffer, 2 ) ;
691+ assert_eq ! ( DynBuffer :: peek( & buffer) , None ) ;
692+ }
574693}
0 commit comments