pipecircuit-cli 0.0.2

Command-line architectural tools for Pipecircuit programs
extern crate alloc;

mod client;
mod wiring;

use alloc::{boxed::Box, vec::Vec};
use app_core::domain_primitives::Amount;
use core::num::{NonZeroU64, NonZeroUsize};
use pipecircuit_mio::worker::{ManagedWorker, WorkerJoined, WorkerStartFailure};
use std::{env, process::ExitCode, thread};

fn main() -> ExitCode {
    let mut additions = Vec::new();
    for argument in env::args_os().skip(1) {
        match argument.to_str().map(str::parse::<NonZeroU64>) {
            Some(Ok(amount)) => additions.push(Amount::new(amount)),
            Some(Err(failure)) => {
                eprintln!("Expected positive u64 additions: {failure}");
                return ExitCode::FAILURE;
            }
            None => {
                eprintln!("Expected a UTF-8 integer, received {argument:?}");
                return ExitCode::FAILURE;
            }
        }
    }
    let wiring::Application {
        mut pipecircuit,
        peer,
        peer_reactor,
    } = match wiring::open(wiring::Configuration {
        requests: NonZeroUsize::MIN,
        replies: NonZeroUsize::MIN,
        retained_replies: NonZeroUsize::MIN,
        dispatch_quantum: NonZeroUsize::MIN,
    }) {
        Ok(application) => application,
        Err(failure) => {
            eprintln!("Installation refused: {failure}");
            return ExitCode::FAILURE;
        }
    };
    let wake = peer.clone_peer_wake();
    let worker = match ManagedWorker::spawn_notifying(
        thread::Builder::new().name("counter-client".into()),
        Box::new(client::Client {
            endpoint: peer,
            reactor: peer_reactor,
            additions,
        }),
        wake,
        |client| client.run(),
    ) {
        Ok(worker) => worker,
        Err(failure) => {
            match failure {
                WorkerStartFailure::Spawn { worker, failure } => {
                    drop(worker);
                    eprintln!("Client thread could not start: {failure}");
                }
                WorkerStartFailure::Handoff { worker, outcome } => {
                    drop(worker);
                    let _reported = report_join(outcome);
                    eprintln!("Client state could not be handed to the thread");
                }
            }
            return ExitCode::FAILURE;
        }
    };

    let conducted = pipecircuit.conduct();
    // Teardown also wakes a waiting client when conduction failed. Replies that
    // were retained at failure are explicitly abandoned, never called delivered.
    let (_, circuit) = pipecircuit.into_parts();
    let (_host, installation) = circuit.into_parts();
    let (pipes, _policy) = installation.into_parts();
    let (endpoint, retained) = pipes.client.into_transport().into_parts();
    let abandoned = retained.len();
    let closed = endpoint.close();
    let joined = report_join(worker.join());
    match (conducted, closed, abandoned, joined) {
        (Ok(wiring::Completion::ClientDisconnected), Ok(()), 0, true) => ExitCode::SUCCESS,
        (conducted, closed, abandoned, _) => {
            eprintln!("Circuit: {conducted:?}; close: {closed:?}; abandoned replies: {abandoned}");
            ExitCode::FAILURE
        }
    }
}

fn report_join(
    outcome: WorkerJoined<pipecircuit_mio::worker::NotifiedWorkerExit<client::Exit>>,
) -> bool {
    match outcome {
        WorkerJoined::Stopped(exit) => {
            let notification = exit.notification;
            match (exit.outcome, notification) {
                (
                    Ok(client::Exit {
                        exchange: Ok(()),
                        close: Ok(()),
                    }),
                    Ok(()),
                ) => true,
                (Ok(exit), notification) => {
                    eprintln!("Client: {exit:?}; completion notification: {notification:?}");
                    false
                }
                (Err(payload), notification) => {
                    eprintln!(
                        "Client unwound: {payload:?}; completion notification: {notification:?}"
                    );
                    false
                }
            }
        }
        WorkerJoined::NotStarted(failure) => {
            eprintln!("Client did not receive its state: {failure}");
            false
        }
        WorkerJoined::Panicked(payload) => {
            eprintln!("Client supervision unwound: {payload:?}");
            false
        }
    }
}