ferrodock-cli 1.28.4

fdock: Ferrodock on the command line. Containers, images, logs and Quick start templates on any Docker engine, with compact JSON for scripts and AI tools.
//! `fdock engine`: the engines Ferrodock knows of, and which one answers;
//! `fdock engine start` starts it.

use ferrodock_core::connect::{self, connect};
use ferrodock_core::engines;
use ferrodock_core::summary::Engine;
use serde::Serialize;

use crate::output;

#[derive(Serialize)]
struct Report {
    engines: Vec<Engine>,
    /// Why no engine answered.
    #[serde(skip_serializing_if = "Option::is_none")]
    error: Option<String>,
}

pub async fn show(name: Option<&str>, json: bool) -> Result<(), String> {
    let picked = connect(name).await;
    let engines: Vec<Engine> = engines()
        .iter()
        .map(|e| {
            let using = picked.as_ref().ok().filter(|c| c.engine.name == e.name);
            Engine {
                name: e.name.clone(),
                host: e.host.clone(),
                in_use: using.is_some(),
                version: using.map(|c| c.version.clone()),
            }
        })
        .collect();
    let error = picked.as_ref().err().cloned();
    if json {
        return output::json(&Report { engines, error });
    }
    let rows: Vec<Vec<String>> = engines
        .iter()
        .map(|e| {
            vec![
                if e.in_use { "*" } else { "" }.to_owned(),
                e.name.clone(),
                e.host.clone(),
            ]
        })
        .collect();
    output::print_table(&["", "NAME", "HOST"], &rows, "No engines.");
    match &picked {
        Ok(c) => println!("\nUsing {} (Docker {}).", c.engine.name, c.version),
        Err(e) => {
            println!("\nNo engine answered: {e}");
            println!("Start it with `fdock engine start`, or pick one with --engine NAME.");
        }
    }
    Ok(())
}

/// Starts the engine `fdock` would use (or the one called `name`) unless
/// one answers already, and waits until it does.
pub async fn start(name: Option<&str>, json: bool) -> Result<(), String> {
    let (conn, started) = connect::start(name, |starter| {
        if !json {
            eprintln!("Starting {}…", starter.name());
        }
    })
    .await?;
    if json {
        return output::json(&serde_json::json!({
            "engine": conn.engine.name,
            "version": conn.version,
            "started": started,
        }));
    }
    let how = if started {
        "Started"
    } else {
        "Already running:"
    };
    println!("{how} {} (Docker {}).", conn.engine.name, conn.version);
    Ok(())
}