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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Stream combinators for declarative message processing
//!
//! This module provides a fluent API for transforming and filtering message streams.
//!
//! # Examples
//!
//! ## Filtering
//!
//! ```rust
//! use mecha10_core::prelude::*;
//! use mecha10_core::topics::perception;
//!
//! # async fn example(ctx: &Context) -> Result<()> {
//! // Only process high-confidence detections
//! let mut detections = ctx.subscribe(perception::DETECTIONS)
//! .await?
//! .filter(|d| d.iter().any(|det| det.confidence > 0.8));
//!
//! while let Some(dets) = detections.recv().await {
//! // All detections have confidence > 0.8
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ## Throttling
//!
//! ```rust
//! use mecha10_core::prelude::*;
//! use mecha10_core::topics::sensor;
//!
//! # async fn example(ctx: &Context) -> Result<()> {
//! // Limit to 10 Hz
//! let mut images = ctx.subscribe(sensor::CAMERA_RGB)
//! .await?
//! .throttle(Duration::from_millis(100))
//! .build();
//!
//! while let Some(image) = images.recv().await {
//! // At most one image every 100ms
//! println!("Image: {}x{}", image.width, image.height);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ## Latest Only
//!
//! ```rust
//! use mecha10_core::prelude::*;
//! use mecha10_core::topics::sensor;
//!
//! # async fn example(ctx: &Context) -> Result<()> {
//! // Always get the latest, drop old messages
//! let mut odom = ctx.subscribe(sensor::ODOMETRY)
//! .await?
//! .latest()
//! .build();
//!
//! while let Some(reading) = odom.recv().await {
//! // Always the most recent odometry reading
//! println!("Position: {:?}", reading.position);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ## Zipping Streams
//!
//! ```rust
//! use mecha10_core::prelude::*;
//! use mecha10_core::topics::sensor;
//!
//! # async fn example(ctx: &Context) -> Result<()> {
//! // Combine camera images with LiDAR scans
//! let mut images = ctx.subscribe(sensor::CAMERA_RGB).await?;
//! let mut scans = ctx.subscribe(sensor::LIDAR_SCAN).await?;
//!
//! let mut zipped = images.zip(scans);
//!
//! while let Some((image, scan)) = zipped.recv().await {
//! // Process synchronized sensor data
//! println!("Image: {}x{}, LiDAR: {} points",
//! image.width, image.height, scan.ranges.len());
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ## Windowing
//!
//! ```rust
//! use mecha10_core::prelude::*;
//! use mecha10_core::topics::perception;
//!
//! # async fn example(ctx: &Context) -> Result<()> {
//! // Collect detection batches over 1-second windows for rate analysis
//! let mut windowed = ctx.subscribe(perception::DETECTIONS)
//! .await?
//! .window(Duration::from_secs(1));
//!
//! while let Some(batches) = windowed.recv().await {
//! let detections: Vec<_> = batches.iter().flatten().collect();
//! let rate = detections.len() as f32; // detections per second
//! println!("Detection rate: {} Hz", rate);
//!
//! // Calculate average confidence over the window
//! let avg_conf = detections.iter()
//! .map(|d| d.confidence)
//! .sum::<f32>() / detections.len() as f32;
//! println!("Average confidence: {:.2}", avg_conf);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! This module is organized into:
//! - `builder`: `StreamBuilder`'s data and pipeline configuration (`filter`, `map`, `throttle`, `debounce`, `latest`)
//! - `pipeline`: `StreamBuilder::build`, running the configured filter/map/throttle/debounce pipeline
//! - `zip`: `StreamBuilder::zip`, pairing two streams
//! - `window`: `StreamBuilder::window`, time-windowed batching
//! - `batch`: `BatchBuilder`, size/timeout-based batching
//! - `receiver_ext`: `ReceiverExt`, the fluent entry point on `Receiver<T>`
pub use BatchBuilder;
pub use StreamBuilder;
pub use ReceiverExt;