use anyhow::Result;
use owo_colors::OwoColorize;
use crate::api::client::ApiClient;
use crate::api::models::{BulkAnswer, BulkRow};
use crate::exit;
use crate::output::Format;
use crate::render::sanitise;
use crate::render::table::{Cell, Table};
use crate::render::theme;
pub async fn run(
client: &ApiClient,
action: &str,
project_reference: &str,
services: &[String],
format: Format,
) -> Result<()> {
let project = client.find_project(project_reference).await?;
let answer = client.bulk(&project.id, action, services).await?;
if format.answered_in_json(&answer.raw) {
return outcome(&answer.body);
}
let body = &answer.body;
println!();
println!(
" {} {} en {} · {}",
theme::ARROW.style(theme::accent()),
verb(&body.action).style(theme::strong()),
sanitise::plain(&body.environment.name).style(theme::strong()),
summary(body).style(theme::muted()),
);
println!();
let mut table = Table::new(&["servicio", "resultado", "detalle"]);
for row in &body.results {
table.push(vec![
Cell::styled(sanitise::plain(&row.name), theme::strong()),
outcome_cell(row),
Cell::styled(detail(row), theme::muted()),
]);
}
table.print_or(format_args!(
"`{}` no tiene servicios en este entorno.",
project.name
));
if body.started > 0 && (body.action == "deploy" || body.action == "restart") {
println!();
println!(
" {}",
format!(
"Sigue cada uno con: fundaia history {} <servicio>",
project.slug
)
.style(theme::muted()),
);
}
println!();
outcome(body)
}
fn outcome(answer: &BulkAnswer) -> Result<()> {
if answer.started == 0 && answer.refused > 0 {
return Err(exit::Refused::invalid(summary(answer)).into());
}
Ok(())
}
fn verb(action: &str) -> &str {
match action {
"deploy" => "Desplegar",
"restart" => "Reiniciar",
"sleep" => "Dormir",
"wake" => "Despertar",
other => other,
}
}
fn summary(answer: &BulkAnswer) -> String {
let started = match answer.started {
1 => "1 en marcha".to_owned(),
count => format!("{count} en marcha"),
};
match answer.refused {
0 => started,
1 => format!("{started}, 1 rechazado"),
count => format!("{started}, {count} rechazados"),
}
}
fn outcome_cell(row: &BulkRow) -> Cell {
if row.outcome == "started" {
Cell::styled(format!("{} en marcha", theme::CHECK), theme::success())
} else {
Cell::styled(format!("{} rechazado", theme::CROSS), theme::danger())
}
}
fn detail(row: &BulkRow) -> String {
match (&row.deployment, &row.reason) {
(Some(deployment), _) => format!("despliegue #{}", deployment.number),
(None, Some(reason)) => sanitise::plain(reason),
(None, None) => "—".to_owned(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::api::models::{BulkDeployment, BulkEnvironment};
fn answer(started: u32, refused: u32) -> BulkAnswer {
BulkAnswer {
action: "deploy".to_owned(),
environment: BulkEnvironment {
id: "env_1".to_owned(),
name: "production".to_owned(),
slug: "production".to_owned(),
},
started,
refused,
results: Vec::new(),
}
}
fn row(deployment: Option<u32>, reason: Option<&str>) -> BulkRow {
BulkRow {
service_environment_id: "sve_1".to_owned(),
name: "web".to_owned(),
outcome: if deployment.is_some() {
"started".to_owned()
} else {
"refused".to_owned()
},
reason: reason.map(str::to_owned),
deployment: deployment.map(|number| BulkDeployment {
id: "dep_1".to_owned(),
number,
}),
}
}
#[test]
fn it_should_count_what_started_and_what_was_refused() {
assert_eq!(summary(&answer(2, 1)), "2 en marcha, 1 rechazado");
}
#[test]
fn it_should_say_only_what_started_when_nothing_was_refused() {
assert_eq!(summary(&answer(3, 0)), "3 en marcha");
}
#[test]
fn it_should_name_the_deployment_it_started() {
assert_eq!(detail(&row(Some(7), None)), "despliegue #7");
}
#[test]
fn it_should_give_the_reason_for_a_refusal() {
assert_eq!(
detail(&row(None, Some("Ya está dormido"))),
"Ya está dormido"
);
}
#[test]
fn it_should_fail_when_every_service_was_refused() {
assert!(outcome(&answer(0, 2)).is_err());
}
#[test]
fn it_should_succeed_when_some_started() {
assert!(outcome(&answer(1, 2)).is_ok());
}
}