haematite-cli 0.21.3

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
use tokio::task::JoinHandle;

pub(super) struct Outcome {
    pub status: i32,
    pub errors: Vec<String>,
}

pub(super) fn gather(result: anyhow::Result<i32>, cleanup: Vec<anyhow::Result<()>>) -> Outcome {
    let (mut status, mut errors) = match result {
        Ok(status) => (status, Vec::new()),
        Err(error) => (1, vec![error.to_string()]),
    };
    errors.extend(
        cleanup
            .into_iter()
            .filter_map(Result::err)
            .map(|error| error.to_string()),
    );
    if status == 0 && !errors.is_empty() {
        status = 1;
    }
    Outcome { status, errors }
}

pub(super) async fn stop<T>(task: &mut JoinHandle<T>, name: &str) -> anyhow::Result<Option<T>> {
    if !task.is_finished() {
        task.abort();
    }
    match task.await {
        Ok(result) => Ok(Some(result)),
        Err(error) if error.is_cancelled() => Ok(None),
        Err(_) => Err(anyhow::anyhow!("{name}_panicked")),
    }
}

#[cfg(test)]
#[path = "closing_tests.rs"]
mod tests;