agent-team-mail-core 1.3.1

Core library for local agent team mail workflows.
Documentation
use std::sync::Arc;

use crate::ack;
use crate::boundary::{self, ClientTransport};
use crate::clear;
use crate::doctor;
use crate::error::AtmError;
use crate::list;
use crate::observability::{
    AtmLogQuery, AtmLogSnapshot, AtmObservabilityHealth, AtmObservabilityHealthState, CommandEvent,
    LogTailSession, ObservabilityPort,
};
use crate::protocol::{
    RequestEnvelope, ResponseEnvelope, SendRequestEnvelope, SendResponseEnvelope,
};
use crate::read;
use crate::send;

#[derive(Clone)]
pub struct FakeClientTransport {
    handler: Arc<dyn Fn(RequestEnvelope) -> Result<ResponseEnvelope, AtmError> + Send + Sync>,
}

impl std::fmt::Debug for FakeClientTransport {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("FakeClientTransport")
            .finish_non_exhaustive()
    }
}

impl FakeClientTransport {
    pub fn new<F>(handler: F) -> Self
    where
        F: Fn(RequestEnvelope) -> Result<ResponseEnvelope, AtmError> + Send + Sync + 'static,
    {
        Self {
            handler: Arc::new(handler),
        }
    }
}

impl boundary::sealed::Sealed for FakeClientTransport {}

impl ClientTransport for FakeClientTransport {
    fn send(&self, request: RequestEnvelope) -> Result<ResponseEnvelope, AtmError> {
        (self.handler)(request)
    }
}

pub struct LoopbackClientTransport {
    observability: Arc<dyn ObservabilityPort + Send + Sync>,
}

impl std::fmt::Debug for LoopbackClientTransport {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("LoopbackClientTransport")
            .finish_non_exhaustive()
    }
}

impl LoopbackClientTransport {
    pub fn new(observability: Arc<dyn ObservabilityPort + Send + Sync>) -> Self {
        Self { observability }
    }
}

impl boundary::sealed::Sealed for LoopbackClientTransport {}

impl ClientTransport for LoopbackClientTransport {
    fn send(&self, request: RequestEnvelope) -> Result<ResponseEnvelope, AtmError> {
        match request {
            RequestEnvelope::Send(SendRequestEnvelope::Compose(request)) => {
                send::send_mail(request, self.observability.as_ref())
                    .map(|outcome| ResponseEnvelope::Send(SendResponseEnvelope::Sent(outcome)))
            }
            RequestEnvelope::Send(SendRequestEnvelope::Acknowledge(request)) => {
                ack::ack_mail(request, self.observability.as_ref()).map(|outcome| {
                    ResponseEnvelope::Send(SendResponseEnvelope::Acknowledged(outcome))
                })
            }
            RequestEnvelope::CompatibilityPreflight(_) => Err(AtmError::daemon_unavailable(
                "loopback compatibility preflight is not wired outside the daemon runtime",
            )),
            RequestEnvelope::Heartbeat(_) => Err(AtmError::daemon_unavailable(
                "loopback heartbeat transport is not wired outside the daemon runtime",
            )),
            RequestEnvelope::List(query) => {
                list::list_mail(query, self.observability.as_ref()).map(ResponseEnvelope::List)
            }
            RequestEnvelope::Peek(query) => read::peek_mail(query, self.observability.as_ref())
                .map(|outcome| ResponseEnvelope::Peek(Box::new(outcome))),
            RequestEnvelope::Receive(query) => read::read_mail(query, self.observability.as_ref())
                .map(|outcome| ResponseEnvelope::Receive(Box::new(outcome))),
            RequestEnvelope::Clear(query) => {
                clear::clear_mail(query, self.observability.as_ref()).map(ResponseEnvelope::Clear)
            }
            RequestEnvelope::Doctor(query) => {
                doctor::run_doctor(query, self.observability.as_ref())
                    .map(|report| ResponseEnvelope::Doctor(Box::new(report)))
            }
        }
    }
}

#[derive(Debug, Default)]
pub struct HealthyObservability;

impl boundary::sealed::Sealed for HealthyObservability {}

impl ObservabilityPort for HealthyObservability {
    fn emit(&self, _event: CommandEvent) -> Result<(), AtmError> {
        Ok(())
    }

    fn query(&self, _req: AtmLogQuery) -> Result<AtmLogSnapshot, AtmError> {
        Ok(AtmLogSnapshot::default())
    }

    fn follow(&self, _req: AtmLogQuery) -> Result<LogTailSession, AtmError> {
        Ok(LogTailSession::empty())
    }

    fn health(&self) -> Result<AtmObservabilityHealth, AtmError> {
        Ok(AtmObservabilityHealth {
            active_log_path: None,
            logging_state: AtmObservabilityHealthState::Healthy,
            query_state: Some(AtmObservabilityHealthState::Healthy),
            maintenance: None,
            diagnostic: None,
            detail: None,
        })
    }
}