use anyhow::Context;
use colored::Colorize;
use dialoguer::Select;
use wasmer_api::backend::BackendClient;
use wasmer_deploy_schema::schema::StringWebcIdent;
pub fn prompt_for_ident(message: &str, default: Option<&str>) -> Result<String, anyhow::Error> {
loop {
let mut diag = dialoguer::Input::new();
diag.with_prompt(message)
.with_initial_text(default.unwrap_or_default());
let raw: String = diag.interact()?;
let val = raw.trim();
if !val.is_empty() {
break Ok(val.to_string());
}
}
}
pub fn prompt_for_package_ident(
message: &str,
default: Option<&str>,
) -> Result<StringWebcIdent, anyhow::Error> {
loop {
let raw: String = dialoguer::Input::new()
.with_prompt(message)
.with_initial_text(default.unwrap_or_default())
.interact_text()
.context("could not read user input")?;
match raw.parse::<StringWebcIdent>() {
Ok(p) => break Ok(p),
Err(err) => {
eprintln!("invalid package name: {err}");
}
}
}
}
pub enum PackageCheckMode {
MustExist,
MustNotExist,
}
pub async fn prompt_for_package(
message: &str,
default: Option<&str>,
check: Option<PackageCheckMode>,
client: Option<&BackendClient>,
) -> Result<(StringWebcIdent, Option<wasmer_api::backend::gql::Package>), anyhow::Error> {
loop {
let name = prompt_for_package_ident(message, default)?;
if let Some(check) = &check {
let api = client.expect("Check mode specified, but no API provided");
let pkg = wasmer_api::backend::get_package(api, name.to_string())
.await
.context("could not query backend for package")?;
match check {
PackageCheckMode::MustExist => {
if let Some(pkg) = pkg {
break Ok((name, Some(pkg)));
} else {
eprintln!("Package '{name}' does not exist");
}
}
PackageCheckMode::MustNotExist => {
if pkg.is_none() {
break Ok((name, None));
} else {
eprintln!("Package '{name}' already exists");
}
}
}
}
}
}
pub fn prompt_for_namespace(
message: &str,
default: Option<&str>,
user: Option<&wasmer_api::backend::gql::UserWithNamespaces>,
) -> Result<String, anyhow::Error> {
if let Some(user) = user {
let namespaces = user
.namespaces
.edges
.clone()
.into_iter()
.flatten()
.filter_map(|e| e.node)
.collect::<Vec<_>>();
let labels = [user.username.clone()]
.into_iter()
.chain(namespaces.iter().map(|ns| ns.global_name.clone()))
.collect::<Vec<_>>();
let selection_index = Select::new()
.with_prompt(message)
.default(0)
.items(&labels)
.interact()
.context("could not read user input")?;
Ok(labels[selection_index].clone())
} else {
loop {
let value = dialoguer::Input::<String>::new()
.with_prompt(message)
.with_initial_text(default.map(|x| x.trim().to_string()).unwrap_or_default())
.interact_text()
.context("could not read user input")?
.trim()
.to_string();
if !value.is_empty() {
break Ok(value);
}
}
}
}
pub async fn prompt_new_app_name(
message: &str,
default: Option<&str>,
namespace: &str,
api: Option<&BackendClient>,
) -> Result<String, anyhow::Error> {
loop {
let ident = prompt_for_ident(message, default)?;
if let Some(api) = &api {
let app =
wasmer_api::backend::get_app(api, namespace.to_string(), ident.clone()).await?;
eprintln!("Checking name availability...");
if app.is_some() {
eprintln!(
"{}: app '{}/{}' already exists - pick a different name",
"WARN:".yellow(),
namespace,
ident
);
} else {
break Ok(ident);
}
}
}
}
pub async fn prompt_new_app_alias(
message: &str,
default: Option<&str>,
api: Option<&BackendClient>,
) -> Result<String, anyhow::Error> {
loop {
let ident = prompt_for_ident(message, default)?;
if let Some(api) = &api {
let app = wasmer_api::backend::get_app_by_alias(api, ident.clone()).await?;
eprintln!("Checking name availability...");
if app.is_some() {
eprintln!(
"{}: alias '{}' already exists - pick a different name",
"WARN:".yellow(),
ident
);
} else {
break Ok(ident);
}
}
}
}