partiri-cli 0.2.0

Partiri CLI — Deploy and manage services on Partiri Cloud
use inquire::Select;

use crate::client::ApiClient;
use crate::error::Result;
use crate::output::{ctx, print_table, ProjectRow};

pub fn run_list(client: &ApiClient, workspace_arg: Option<String>) -> Result<()> {
    let workspace_id = match workspace_arg {
        Some(id) => id,
        None => resolve_workspace(client)?,
    };

    let projects = client.list_projects(&workspace_id)?;

    let rows: Vec<ProjectRow> = projects
        .into_iter()
        .map(|p| ProjectRow {
            name: p.name,
            environment: p.environment,
            id: p.id,
        })
        .collect();

    if rows.is_empty() && !ctx().json {
        println!("No projects found in this workspace.");
        return Ok(());
    }

    print_table(rows);
    Ok(())
}

fn resolve_workspace(client: &ApiClient) -> Result<String> {
    let workspaces = client.list_workspaces()?;
    if workspaces.is_empty() {
        return Err("No workspaces found. Create one at https://partiri.cloud".into());
    }
    if workspaces.len() == 1 {
        return Ok(workspaces[0].id.clone());
    }
    if ctx().no_input {
        return Err(
            "Multiple workspaces — pass --workspace <UUID>. Run 'partiri workspaces list' to see them."
                .into(),
        );
    }

    // Disambiguate by UUID — `email` is only populated on the user's primary workspace
    // and would render as "Name ()" for the others, breaking the find lookup.
    let options: Vec<String> = workspaces
        .iter()
        .map(|w| format!("{} ({})", w.name, w.id))
        .collect();
    let choice = Select::new("Select workspace:", options.clone())
        .prompt()
        .map_err(|e| format!("Selection cancelled: {e}"))?;
    let (_, workspace) = options
        .into_iter()
        .zip(workspaces.into_iter())
        .find(|(label, _)| label == &choice)
        .ok_or("Selected workspace not found in list")?;
    Ok(workspace.id)
}