pipecircuit-cli 0.0.13

Command-line architectural tools for Pipecircuit programs
use core::convert::Infallible;

use __CRATE___core as app_core;
use __CRATE___pipes as app_pipes;
use __CRATE___switchboard as app_routes;

use app_core::{
    domain_primitives::Activation,
    state_machines::latch::{Activate, Latch},
};
use app_pipes::{Activator, Client};
use app_routes::{ActivationProgram, From, To};
use pipecircuit::{CircuitSendOutcome, Inline, InstalledPipe};
use pipecircuit_browser::{WorkerExchangeFailure, flow_to_worker_reply};

pub struct WorkerProgram {
    switchboard: ActivationProgram,
    activator: InstalledPipe<Activator, Inline<Latch, Activator>>,
}

impl WorkerProgram {
    pub fn exchange(
        &mut self,
        command: Activate,
    ) -> Result<Activation, WorkerExchangeFailure<Infallible, To<'static>>> {
        flow_to_worker_reply::<Client, _, _, 0>(
            &mut self.switchboard,
            command,
            &mut pipecircuit::PendingRoutes::new(),
            From::Client,
            |destination| match destination {
                To::Activator(input) => Ok(CircuitSendOutcome::Immediate {
                    from: From::Activator(self.activator.send(input)),
                }),
                To::Client(output) => Ok(CircuitSendOutcome::Completed { completion: output }),
            },
        )
    }
}

fn initialize() -> Result<WorkerProgram, Infallible> {
    Ok(WorkerProgram {
        switchboard: ActivationProgram,
        activator: InstalledPipe::new(Inline::for_pipe(Latch::default())),
    })
}

pipecircuit_browser::export_web_worker_program!(
    pub ActivationWorker,
    participant = Client,
    program = WorkerProgram,
    initialize = initialize,
);

#[cfg(test)]
mod tests {
    use super::*;
    use pipecircuit_browser::{PostcardByteProgramExchangeError, WorkerBoundary};

    #[test]
    fn successive_messages_reach_the_same_authority() -> Result<(), Infallible> {
        let mut program = initialize()?;
        assert!(matches!(
            program.exchange(Activate),
            Ok(Activation::Activated)
        ));
        assert!(matches!(
            program.exchange(Activate),
            Ok(Activation::AlreadyActive)
        ));
        Ok(())
    }

    #[test]
    fn invalid_bytes_do_not_mutate_the_worker() -> Result<(), Infallible> {
        let mut boundary = WorkerBoundary::<Client, _>::new(initialize()?);
        // A unit command is an empty Postcard payload; any byte is trailing data.
        assert!(matches!(
            boundary.exchange(&[1], WorkerProgram::exchange),
            Err(PostcardByteProgramExchangeError::TrailingCommandBytes { byte_count: 1 })
        ));
        assert!(
            matches!(boundary.exchange(&[], WorkerProgram::exchange), Ok(bytes) if bytes == [0])
        );
        assert!(
            matches!(boundary.exchange(&[], WorkerProgram::exchange), Ok(bytes) if bytes == [1])
        );
        Ok(())
    }
}