kcode-audio-session-ingress 0.3.3

Application-level facade over AudioIngress and exact Session History handoff
Documentation
use kcode_audio_history_handoff::RecordingProjection;
use kcode_audio_ingress::{
    LegacyReviewDisposition, RecordingState, RecordingStatus, legacy_review_disposition,
};
use uuid::Uuid;

use crate::{Coordinator, Error, audio_error, handoff_error, recording_belongs_to};

impl Coordinator {
    pub(crate) fn owned_recordings(&self) -> Result<Vec<RecordingStatus>, Error> {
        Ok(self
            .audio
            .status()
            .map_err(audio_error)?
            .recordings
            .into_iter()
            .filter(|recording| recording_belongs_to(recording, &self.user_id))
            .collect())
    }

    pub(crate) async fn project_recordings(
        &self,
        recordings: &[RecordingStatus],
    ) -> Result<Vec<RecordingProjection>, Error> {
        let projections = self
            .handoff
            .project(recordings)
            .await
            .map_err(handoff_error)?;
        if recordings.len() != projections.len() {
            return Err(Error::internal("handoff returned incomplete projections"));
        }
        Ok(projections)
    }

    pub(crate) async fn resolve_legacy_reviews(
        &self,
        recordings: &[RecordingStatus],
        projections: &[RecordingProjection],
    ) -> Result<bool, Error> {
        let decisions = decisions(recordings, projections);
        for (recording_id, disposition) in &decisions {
            self.audio
                .resolve_legacy_review(*recording_id, *disposition)
                .map_err(audio_error)?;
        }
        Ok(!decisions.is_empty())
    }

    pub(crate) async fn prepared_recordings(
        &self,
    ) -> Result<(Vec<RecordingStatus>, Vec<RecordingProjection>), Error> {
        let mut recordings = self.owned_recordings()?;
        let mut projections = self.project_recordings(&recordings).await?;
        if self
            .resolve_legacy_reviews(&recordings, &projections)
            .await?
        {
            recordings = self.owned_recordings()?;
            projections = self.project_recordings(&recordings).await?;
        }
        Ok((recordings, projections))
    }
}

pub(crate) fn decisions(
    recordings: &[RecordingStatus],
    projections: &[RecordingProjection],
) -> Vec<(Uuid, LegacyReviewDisposition)> {
    recordings
        .iter()
        .zip(projections)
        .filter_map(|(recording, projection)| {
            debug_assert_eq!(recording.id, projection.recording_id);
            disposition(recording, !projection.pieces.is_empty())
                .map(|disposition| (recording.id, disposition))
        })
        .collect()
}

pub(crate) fn has_candidates(recordings: &[RecordingStatus]) -> bool {
    recordings
        .iter()
        .any(|recording| disposition(recording, false).is_some())
}

fn disposition(recording: &RecordingStatus, has_ingress: bool) -> Option<LegacyReviewDisposition> {
    legacy_review_disposition(
        matches!(&recording.state, RecordingState::Complete { .. }),
        recording
            .correction_packet
            .as_ref()
            .map(|packet| packet.confirmation_state),
        has_ingress,
    )
}