cloudformatious 0.7.0

Extension traits for rusoto_cloudformation
Documentation
use std::{convert::TryInto, env, io, process};

use cloudformatious::{ApplyStackInput, Client, DeleteStackInput, TemplateSource};
use futures_util::StreamExt;

const USAGE: &str = "Usage: cargo run --example cli -- <apply|delete> <stack_name> [template_body]";

#[tokio::main]
async fn main() {
    if let Err(error) = try_main().await {
        eprintln!("{}", error);
        process::exit(1);
    }
}

async fn try_main() -> Result<(), Box<dyn std::error::Error>> {
    let [op, stack_name]: [_; 2] = env::args()
        .skip(1)
        .take(2)
        .collect::<Vec<_>>()
        .try_into()
        .map_err(|_| USAGE)?;

    let config = aws_config::load_from_env().await;
    let client = Client::new(&config);

    match op.as_str() {
        "apply" => {
            let template_body = env::args().nth(3).ok_or(USAGE)?;

            let input = ApplyStackInput::new(stack_name, TemplateSource::inline(template_body));
            let mut apply = client.apply_stack(input);

            let change_set = apply.change_set().await?;
            eprintln!("=== Change set ===");
            eprintln!("{:#?}", change_set);
            eprintln!();

            confirm("Continue [Y/n]?").await?;
            eprintln!();

            let mut events = apply.events();
            while let Some(event) = events.next().await {
                eprintln!("{:?}", event);
            }
            eprintln!();

            let output = apply.await?;
            eprintln!("=== Operation succeeded ===");
            eprintln!("{:#?}", output);
            eprintln!();
        }
        "delete" => {
            if env::args().nth(3).is_some() {
                return Err(USAGE.into());
            }

            let input = DeleteStackInput::new(stack_name);
            let mut delete = client.delete_stack(input);

            let mut events = delete.events();
            while let Some(event) = events.next().await {
                eprintln!("{:?}", event);
            }
            eprintln!();

            delete.await?;
            eprintln!("=== Operation succeeded ===");
        }
        _ => return Err(USAGE.into()),
    }

    Ok(())
}

async fn confirm(prompt: &str) -> io::Result<()> {
    eprint!("{} ", prompt);

    tokio::task::spawn_blocking(|| {
        let mut input = String::new();
        io::stdin().read_line(&mut input)?;
        match input.as_str().trim() {
            "Y" | "y" | "" => Ok(()),
            _ => Err(io::Error::new(io::ErrorKind::Other, "Quitting")),
        }
    })
    .await
    .unwrap()
}