visual-cortex-capture 0.5.0

Screen-capture abstraction for visual-cortex: frames, regions, rates, the FrameSource trait, and the macOS ScreenCaptureKit backend.
use std::collections::VecDeque;
use std::sync::Arc;

use crate::error::CaptureError;
use crate::frame::Frame;
use crate::source::FrameSource;

/// A scripted `FrameSource` for tests and demos: yields its frames in order,
/// then repeats the final frame forever.
pub struct FakeSource {
    script: VecDeque<Arc<Frame>>,
    last: Option<Arc<Frame>>,
}

impl FakeSource {
    pub fn new(frames: impl IntoIterator<Item = Frame>) -> Self {
        Self {
            script: frames.into_iter().map(Arc::new).collect(),
            last: None,
        }
    }
}

#[async_trait::async_trait]
impl FrameSource for FakeSource {
    async fn capture_frame(&mut self) -> Result<Arc<Frame>, CaptureError> {
        if let Some(frame) = self.script.pop_front() {
            self.last = Some(frame.clone());
            return Ok(frame);
        }
        self.last.clone().ok_or(CaptureError::Exhausted)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn replays_script_then_repeats_last_frame() {
        let mut src = FakeSource::new([
            Frame::solid(2, 2, [1, 1, 1, 255]),
            Frame::solid(2, 2, [2, 2, 2, 255]),
        ]);
        assert_eq!(src.capture_frame().await.unwrap().data()[0], 1);
        assert_eq!(src.capture_frame().await.unwrap().data()[0], 2);
        // script exhausted -> repeats last forever
        assert_eq!(src.capture_frame().await.unwrap().data()[0], 2);
        assert_eq!(src.capture_frame().await.unwrap().data()[0], 2);
    }

    #[tokio::test]
    async fn empty_source_is_exhausted() {
        let mut src = FakeSource::new([]);
        assert!(matches!(
            src.capture_frame().await,
            Err(CaptureError::Exhausted)
        ));
    }
}