Skip to content

Commit

Permalink
Interactive create command. Closes #26
Browse files Browse the repository at this point in the history
  • Loading branch information
adamrodger committed Jul 26, 2020
1 parent 9c044b1 commit c838d64
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 11 deletions.
56 changes: 55 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion gctx/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "gctx"
version = "0.4.0"
version = "0.5.0"
authors = ["Adam Rodger <crates@adamrodger.com>"]
edition = "2018"
description = "A gcloud configuration management utility"
Expand All @@ -17,6 +17,7 @@ categories = ["command-line-utilities", "config"]
anyhow = "1"
clap = "3.0.0-beta.1"
colored = "2"
dialoguer = "0.6"
gcloud-ctx = { path = "../gcloud-ctx", version = "0.3" }
which = "4"

Expand Down
13 changes: 9 additions & 4 deletions gctx/src/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,25 @@ pub enum SubCommand {

/// Create a new configuration
Create {
/// Create a configuration interactively
#[clap(short, long, conflicts_with_all(&["name", "project", "account", "zone", "region", "activate", "force"]))]
interactive: bool,

// Name of the new configuration
name: String,
#[clap(required_unless("interactive"), requires_all(&["project", "account", "zone"]))]
name: Option<String>,

/// Setting for core/project
#[clap(short, long)]
project: String,
project: Option<String>,

/// Setting for core/account
#[clap(short, long)]
account: String,
account: Option<String>,

/// Setting for compute/zone
#[clap(short, long)]
zone: String,
zone: Option<String>,

/// Setting for compute/region
#[clap(short, long)]
Expand Down
58 changes: 57 additions & 1 deletion gctx/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use anyhow::{Context, Result};
use anyhow::{bail, Context, Result};
use colored::*;
use dialoguer::{Confirm, Input};
use gcloud_ctx::{ConfigurationStore, ConflictAction, PropertiesBuilder};

/// Used to control whether to activate a configuration after creation
Expand Down Expand Up @@ -66,6 +67,61 @@ pub fn copy(src_name: &str, dest_name: &str, conflict: ConflictAction, activate:
Ok(())
}

/// Create a new configuration interactively
pub fn create_interactive() -> Result<()> {
let store = ConfigurationStore::with_default_location()?;

let name = Input::<String>::new()
.with_prompt("Name".blue().to_string())
.interact()?;

if store.find_by_name(&name).is_some() {
let prompt = "A configuration with the same name already exists. Overwrite?"
.yellow()
.to_string();
let confirm = Confirm::new().with_prompt(prompt).default(false).interact()?;

if !confirm {
bail!("Operation cancelled".yellow());
}
}

let project = Input::<String>::new()
.with_prompt("Project".blue().to_string())
.interact()?;

let account = Input::<String>::new()
.with_prompt("Account".blue().to_string())
.interact()?;

let zone = Input::<String>::new()
.with_prompt("Zone".blue().to_string())
.interact()?;

let region = Input::<String>::new()
.with_prompt("Region (optional)".blue().to_string())
.allow_empty(true)
.interact()?;
let region = if region.is_empty() { None } else { Some(region) };

let activate = Confirm::new()
.with_prompt("Activate".blue().to_string())
.default(false)
.interact()?;

create(
&name,
&project,
&account,
&zone,
region.as_deref(),
ConflictAction::Overwrite,
activate.into(),
)?;

Ok(())
}

/// Create a new configuration
pub fn create(
name: &str,
Expand Down
11 changes: 7 additions & 4 deletions gctx/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ pub fn run(opts: Opts) -> Result<()> {
} => {
commands::copy(&src_name, &dest_name, force.into(), activate.into())?;
}
SubCommand::Create { interactive: true, .. } => commands::create_interactive()?,
SubCommand::Create {
interactive: false,
name,
project,
account,
Expand All @@ -43,10 +45,11 @@ pub fn run(opts: Opts) -> Result<()> {
force,
} => {
commands::create(
&name,
&project,
&account,
&zone,
// safe to unwrap these because they are set as required in clap
&name.unwrap(),
&project.unwrap(),
&account.unwrap(),
&zone.unwrap(),
region.as_deref(),
force.into(),
activate.into(),
Expand Down
65 changes: 65 additions & 0 deletions gctx/tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,71 @@ fn create_without_force_fails() {
tmp.close().unwrap();
}

#[test]
#[ignore] // TODO: this doesn't work because assert_cmd doesn't support interactive programs
fn create_interactive_with_activate() {
let (mut cli, tmp) = TempConfigurationStore::new()
.unwrap()
.with_config_activated("foo")
.build()
.unwrap();

#[rustfmt::skip]
cli.arg("create").arg("--interactive")
.write_stdin("bar") // name
.write_stdin("my-project") // project
.write_stdin("a.user@example.org") // account
.write_stdin("europe-west1-d") // zone
.write_stdin("us-east1") // region
.write_stdin("y"); // activate

cli.assert()
.success()
.stdout("Successfully created configuration 'bar'\n");

tmp.child("active_config").assert("bar");

#[rustfmt::skip]
tmp.child("configurations/config_bar").assert([
"[core]",
"project=my-project",
"account=a.user@example.org",
"[compute]",
"zone=europe-west1-d",
"region=us-east1",
""
].join("\n"));

tmp.close().unwrap();
}

#[test]
#[ignore] // TODO: this doesn't work because assert_cmd doesn't support interactive programs
fn create_interactive_without_activate() {
let (mut cli, tmp) = TempConfigurationStore::new()
.unwrap()
.with_config_activated("foo")
.build()
.unwrap();

#[rustfmt::skip]
cli.arg("create").arg("--interactive")
.write_stdin("bar") // name
.write_stdin("my-project") // project
.write_stdin("a.user@example.org") // account
.write_stdin("europe-west1-d") // zone
.write_stdin("us-east1") // region
.write_stdin("n"); // activate

cli.assert()
.success()
.stdout("Successfully created configuration 'bar'\n");

tmp.child("active_config").assert("foo");

tmp.close().unwrap();
}

#[test]
fn describe_with_name_shows_supported_properties() {
let (mut cli, tmp) = TempConfigurationStore::new()
Expand Down

0 comments on commit c838d64

Please sign in to comment.