pocketstation 1.0.1

Source-aware desktop audio Session SDK
Documentation
//! Bounded operator ingress and named output contracts.

#[cfg(any(test, feature = "internal-testing"))]
use std::sync::atomic::Ordering;
#[cfg(any(test, feature = "internal-testing"))]
use std::sync::Arc;

#[cfg(any(test, feature = "internal-testing"))]
use crate::frame::AudioFrame;
#[cfg(any(test, feature = "internal-testing"))]
use crate::graph::SignalEnvelope;
use crate::graph::{EdgeContract, EdgeId, MediaCaps, SignalSpec};

#[cfg(any(test, feature = "internal-testing"))]
use super::edge::{SignalEdgeSendError, SignalEdgeSender};
use super::edge::{
    TypedEdgeBranchSpec, TypedEdgeObservationHandle, TypedEdgeObservations, TypedEdgeReceiver,
};
#[cfg(any(test, feature = "internal-testing"))]
use super::observations::AsyncOperatorObservationState;

#[cfg(any(test, feature = "internal-testing"))]
pub struct AsyncOperatorInput {
    pub(super) sender: SignalEdgeSender<SignalEnvelope>,
    pub(super) observations: Arc<AsyncOperatorObservationState>,
}

#[cfg(any(test, feature = "internal-testing"))]
#[derive(Debug, thiserror::Error)]
#[error("this async operator consumes a compiled plan edge and has no direct input sender")]
pub struct AsyncOperatorInputAccessError;

#[cfg(any(test, feature = "internal-testing"))]
impl AsyncOperatorInput {
    #[expect(
        clippy::result_large_err,
        reason = "full queues return the rejected envelope inline so saturation never allocates"
    )]
    pub fn send(
        &mut self,
        envelope: SignalEnvelope,
    ) -> Result<(), SignalEdgeSendError<SignalEnvelope>> {
        self.observations
            .input_attempted_total
            .fetch_add(1, Ordering::Relaxed);
        let result = self.sender.send(envelope);
        if result.is_err() {
            self.observations
                .input_dropped_total
                .fetch_add(1, Ordering::Relaxed);
        }
        result
    }

    pub fn send_audio(
        &mut self,
        frame: AudioFrame,
        sequence_number: u64,
        timestamp_ns: u64,
    ) -> Result<(), AudioFrame> {
        self.observations
            .input_attempted_total
            .fetch_add(1, Ordering::Relaxed);
        let result = self.sender.send_audio(frame, sequence_number, timestamp_ns);
        if result.is_err() {
            self.observations
                .input_dropped_total
                .fetch_add(1, Ordering::Relaxed);
        }
        result
    }
}

pub type AsyncOperatorOutput = TypedEdgeReceiver;
pub type AsyncOperatorOutputObservationHandle = TypedEdgeObservationHandle;
pub type AsyncOperatorOutputBranchSpec = TypedEdgeBranchSpec;
pub type AsyncOperatorOutputObservations = TypedEdgeObservations;

pub struct AsyncOperatorTypedInput {
    pub port_name: String,
    pub receiver: TypedEdgeReceiver,
    pub edge_id: Option<EdgeId>,
    pub signal_spec: SignalSpec,
    pub media: MediaCaps,
    pub edge_contract: EdgeContract,
    pub capacity_signals: usize,
}

#[derive(Debug, Clone, Copy)]
pub struct AsyncOperatorNamedOutputBranchSpec<'a> {
    pub output_port: &'a str,
    pub branch: AsyncOperatorOutputBranchSpec,
}

pub struct AsyncOperatorNamedOutput {
    pub output_port: String,
    pub receiver: AsyncOperatorOutput,
}