use crate::commands::RobotCommand;
use crate::context::CliContext;
use crate::handlers::config::create_config_client;
use anyhow::{Context, Result};
use console::style;
use dialoguer::Confirm;
use mecha10_robot_config::RobotSummary;
pub async fn handle_robot(ctx: &mut CliContext, cmd: &RobotCommand) -> Result<()> {
match cmd {
RobotCommand::List => handle_robot_list(ctx).await,
RobotCommand::Delete { name, yes } => handle_robot_delete(ctx, name, *yes).await,
}
}
pub async fn handle_robot_list(ctx: &mut CliContext) -> Result<()> {
let client = create_config_client(ctx).await?;
let robots = client.list_robots().await.context("Failed to list robots")?;
if robots.is_empty() {
println!("No robots registered.");
return Ok(());
}
println!();
println!("{}", style("Robots").bold());
println!("{}", style("━".repeat(50)).dim());
for robot in &robots {
println!(
" {} {} ({})",
style("•").cyan(),
style(&robot.name).bold(),
style(&robot.id).dim()
);
}
println!();
Ok(())
}
fn resolve_robot_by_name<'a>(robots: &'a [RobotSummary], name: &str) -> Result<&'a RobotSummary> {
robots
.iter()
.find(|r| r.name == name)
.ok_or_else(|| anyhow::anyhow!("No robot named '{}' found for this account.", name))
}
pub async fn handle_robot_delete(ctx: &mut CliContext, name: &str, yes: bool) -> Result<()> {
let client = create_config_client(ctx).await?;
let robots = client.list_robots().await.context("Failed to list robots")?;
let robot = resolve_robot_by_name(&robots, name)?;
let id = robot.id.clone();
if !yes {
let confirm = Confirm::new()
.with_prompt(format!(
"Delete robot '{}'? Training runs, models, and configs are NOT deleted, and the name \
can be reused for a new robot afterward.",
name
))
.default(false)
.interact()?;
if !confirm {
println!("Cancelled.");
return Ok(());
}
}
let deleted = client.delete_robot(&id).await.context("Failed to delete robot")?;
println!(
"{} Deleted robot '{}'. Training runs, models, and configs for this robot were not deleted, and the name \
is available to register again.",
style("✓").green().bold(),
deleted.name
);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn robot(id: &str, name: &str) -> RobotSummary {
RobotSummary {
id: id.to_string(),
name: name.to_string(),
}
}
#[test]
fn resolve_robot_by_name_finds_matching_robot() {
let robots = vec![robot("r1", "rover-1"), robot("r2", "rover-2")];
let found = resolve_robot_by_name(&robots, "rover-2").unwrap();
assert_eq!(found.id, "r2");
}
#[test]
fn resolve_robot_by_name_errors_when_not_found() {
let robots = vec![robot("r1", "rover-1")];
let err = resolve_robot_by_name(&robots, "missing").unwrap_err();
assert!(err.to_string().contains("No robot named 'missing'"));
}
#[test]
fn resolve_robot_by_name_errors_on_empty_list() {
let robots: Vec<RobotSummary> = vec![];
let err = resolve_robot_by_name(&robots, "rover-1").unwrap_err();
assert!(err.to_string().contains("No robot named 'rover-1'"));
}
}