use inquire::{Select, Text};
use crate::client::ApiClient;
use crate::error::{CliError, Result};
use crate::output::{ctx, print_success};
pub struct CreateArgs {
pub workspace: Option<String>,
pub name: Option<String>,
pub environment: Option<String>,
}
pub fn run_create(client: &ApiClient, args: CreateArgs) -> Result<()> {
let workspace_id = match args.workspace {
Some(id) => id,
None => resolve_workspace(client)?,
};
let name = match args.name {
Some(n) => n,
None => {
if ctx().no_input {
return Err(Box::new(
CliError::new(
"validation",
"--name is required when running non-interactively.",
)
.enriched(),
));
}
Text::new("Project name:").prompt().map_err(|_| {
Box::new(CliError::new("cancelled", "Operation cancelled by user."))
as crate::error::Error
})?
}
};
let environment = match args.environment {
Some(e) => normalise_environment(&e)?,
None => {
if ctx().no_input {
return Err(Box::new(
CliError::new(
"validation",
"--environment is required when running non-interactively. Allowed values: dev, staging, prod.",
)
.enriched(),
));
}
let env_options = vec!["Development", "Staging", "Production"];
let env_choice = Select::new("Environment:", env_options)
.prompt()
.map_err(|_| {
Box::new(CliError::new("cancelled", "Operation cancelled by user."))
as crate::error::Error
})?;
match env_choice {
"Development" => "dev",
"Staging" => "staging",
"Production" => "prod",
_ => unreachable!(),
}
.to_string()
}
};
client.create_project(&name, &environment, &workspace_id)?;
print_success(&format!("Project '{}' created.", name));
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(),
);
}
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)
}
fn normalise_environment(input: &str) -> Result<String> {
match input.to_ascii_lowercase().as_str() {
"dev" | "development" => Ok("dev".to_string()),
"staging" | "stage" => Ok("staging".to_string()),
"prod" | "production" => Ok("prod".to_string()),
_ => Err(format!(
"invalid --environment '{}'. Allowed: dev, staging, prod.",
input
)
.into()),
}
}