mecha10-core 0.6.5

Mecha10 core runtime and foundational types
Documentation
//! `StreamBuilder::window`: time-windowed batching.

use super::StreamBuilder;
use crate::context::Receiver;
use crate::messages::Message;
use std::time::Duration;
use tokio::sync::mpsc;

impl<T: Message + Send + 'static> StreamBuilder<T> {
    /// Window messages over a time period
    ///
    /// Collects messages over a fixed time window and emits them as a vector.
    /// This is useful for time-based aggregation and analysis.
    ///
    /// # Arguments
    ///
    /// * `window_duration` - Duration of the time window
    ///
    /// # Returns
    ///
    /// A `Receiver<Vec<T>>` that yields vectors of messages collected in each window
    ///
    /// # Example
    ///
    /// ```rust
    /// # use mecha10_core::prelude::*;
    /// # async fn example(ctx: &Context) -> Result<()> {
    /// // Collect detections over 1-second windows
    /// let mut windowed = ctx.subscribe(perception::DETECTIONS)
    ///     .window(Duration::from_secs(1));
    ///
    /// while let Some(detections) = windowed.recv().await {
    ///     // Process all detections from the last second
    ///     println!("Received {} detections in 1 second window", detections.len());
    ///
    ///     // Example: Calculate average confidence
    ///     let avg_confidence = detections.iter()
    ///         .map(|d| d.confidence)
    ///         .sum::<f32>() / detections.len() as f32;
    ///     println!("Average confidence: {:.2}", avg_confidence);
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Use Cases
    ///
    /// - **Rate calculation**: Count messages per time window
    /// - **Moving averages**: Compute statistics over windows
    /// - **Burst detection**: Identify spikes in message frequency
    /// - **Time-series analysis**: Analyze temporal patterns
    pub fn window(self, window_duration: Duration) -> Receiver<Vec<T>>
    where
        T: Clone,
    {
        let (tx, rx) = mpsc::unbounded_channel();

        tokio::spawn(async move {
            Self::run_window_pipeline(self.receiver, tx, window_duration).await;
        });

        Receiver::from(rx)
    }

    /// Internal method to run the window pipeline
    async fn run_window_pipeline(
        mut receiver: Receiver<T>,
        tx: mpsc::UnboundedSender<Vec<T>>,
        window_duration: Duration,
    ) where
        T: Clone,
    {
        let mut window = Vec::new();
        let mut deadline = tokio::time::Instant::now() + window_duration;

        loop {
            tokio::select! {
                msg_opt = receiver.recv() => {
                    match msg_opt {
                        Some(msg) => {
                            window.push(msg);
                        }
                        None => {
                            if !window.is_empty() {
                                let _ = tx.send(window);
                            }
                            break;
                        }
                    }
                }

                _ = tokio::time::sleep_until(deadline) => {
                    if tx.send(window.clone()).is_err() {
                        break;
                    }
                    window.clear();
                    deadline = tokio::time::Instant::now() + window_duration;
                }
            }
        }
    }
}