hcctl/monitor: use clap derive macros (#7)

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Aditya Wasan 2020-11-05 00:52:33 +05:30 committed by GitHub
parent 72c8a5d019
commit af643d1673
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 118 additions and 59 deletions

3
.gitignore vendored
View File

@ -26,3 +26,6 @@ target/
.history .history
# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode # End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode
### CLion ###
.idea/*

54
Cargo.lock generated
View File

@ -133,7 +133,9 @@ checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142"
dependencies = [ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
"clap_derive",
"indexmap", "indexmap",
"lazy_static",
"os_str_bytes", "os_str_bytes",
"strsim", "strsim",
"termcolor", "termcolor",
@ -142,6 +144,19 @@ dependencies = [
"vec_map", "vec_map",
] ]
[[package]]
name = "clap_derive"
version = "3.0.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "const_fn" name = "const_fn"
version = "0.4.2" version = "0.4.2"
@ -294,6 +309,15 @@ dependencies = [
"healthchecks", "healthchecks",
] ]
[[package]]
name = "heck"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
dependencies = [
"unicode-segmentation",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.16" version = "0.1.16"
@ -423,6 +447,30 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]] [[package]]
name = "proc-macro-hack" name = "proc-macro-hack"
version = "0.5.18" version = "0.5.18"
@ -810,6 +858,12 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.8" version = "0.1.8"

View File

@ -9,6 +9,6 @@ edition = "2018"
[dependencies] [dependencies]
anyhow = "1.0.33" anyhow = "1.0.33"
chrono = "0.4.19" chrono = "0.4.19"
clap = { version = "3.0.0-beta.2", default-features = false, features = ["std", "suggestions", "color"] } clap = "3.0.0-beta.2"
healthchecks = { path = "../healthchecks", version = "^1.0.2-alpha.0"} healthchecks = { path = "../healthchecks", version = "^1.0.2-alpha.0"}
prettytable-rs = "0.8.0" prettytable-rs = "0.8.0"

View File

@ -4,10 +4,9 @@ extern crate prettytable;
use std::env::var; use std::env::var;
use std::time::SystemTime; use std::time::SystemTime;
use anyhow::anyhow;
use chrono::prelude::{DateTime, Datelike, Timelike}; use chrono::prelude::{DateTime, Datelike, Timelike};
use chrono::Duration; use chrono::Duration;
use clap::{App, AppSettings, Arg}; use clap::{crate_version, Clap};
use prettytable::{format, Table}; use prettytable::{format, Table};
use healthchecks::manage; use healthchecks::manage;
@ -18,6 +17,31 @@ struct Settings {
ua: Option<String>, ua: Option<String>,
} }
/// Command-line tool for interacting with a https://healthchecks.io account
#[derive(Clap)]
#[clap(version = crate_version!())]
struct Opts {
#[clap(subcommand)]
subcommand: SubCommand,
}
#[derive(Clap)]
enum SubCommand {
List(List),
Pings(Pings),
}
/// Lists the checks in your account with their last ping
#[derive(Clap)]
struct List {}
/// Get the last 10 pings for the given check ID
#[derive(Clap)]
struct Pings {
/// ID of the check whose pings are being fetched
check_id: String,
}
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let ua = match var("HEALTHCHECKS_USERAGENT") { let ua = match var("HEALTHCHECKS_USERAGENT") {
Ok(f) => Some(f), Ok(f) => Some(f),
@ -27,28 +51,15 @@ fn main() -> anyhow::Result<()> {
token: var("HEALTHCHECKS_TOKEN").expect("HEALTHCHECKS_TOKEN must be set to run monitor"), token: var("HEALTHCHECKS_TOKEN").expect("HEALTHCHECKS_TOKEN must be set to run monitor"),
ua, ua,
}; };
let opts = Opts::parse();
let matches = App::new("hcctl") match opts.subcommand {
.about("Command-line tool for interacting with a https://healthchecks.io account") SubCommand::List(_) => {
.version(env!("CARGO_PKG_VERSION")) list(settings)?;
.setting(AppSettings::ColoredHelp) }
.setting(AppSettings::DeriveDisplayOrder) SubCommand::Pings(p) => {
.setting(AppSettings::SubcommandRequiredElseHelp) pings(settings, &p.check_id)?;
.subcommand(App::new("list").about("Lists the checks in your account with their last ping")) }
.subcommand(
App::new("pings")
.about("Get the last 10 pings for the given check ID")
.args(&[Arg::new("check_id")
.about("ID of the check whose pings are being fetched")
.required(true)
.index(1)]),
)
.get_matches();
match matches.subcommand().unwrap() {
("list", _) => list(settings)?,
("pings", matches) => pings(settings, matches.value_of("check_id").unwrap())?,
(cmd, _) => return Err(anyhow!("unknown subcommand: {}", cmd)),
} }
Ok(()) Ok(())

View File

@ -14,7 +14,7 @@ readme = "README.md"
[dependencies] [dependencies]
anyhow = "1.0.33" anyhow = "1.0.33"
clap = { version = "3.0.0-beta.2", default-features = false, features = ["std", "suggestions", "color"] } clap = "3.0.0-beta.2"
healthchecks = { path = "../healthchecks", version = "^1.0.2-alpha.0"} healthchecks = { path = "../healthchecks", version = "^1.0.2-alpha.0"}
[badges] [badges]

View File

@ -1,5 +1,5 @@
use anyhow::Context; use anyhow::Context;
use clap::{App, AppSettings, Arg}; use clap::{crate_version, Clap};
use healthchecks::ping::get_config; use healthchecks::ping::get_config;
use std::env::var; use std::env::var;
use std::process::Command; use std::process::Command;
@ -10,6 +10,18 @@ struct Settings {
ua: Option<String>, ua: Option<String>,
} }
/// monitor runs the given command and reports execution result to https://healthchecks.io
#[derive(Clap)]
#[clap(version = crate_version!(), author = "Harsh Shandilya <me@msfjarvis.dev>")]
struct Opts {
/// command to execute and monitor
#[clap(short = 'X', long = "exec")]
command: Vec<String>,
/// starts a timer before running the command
#[clap(short = 't', long = "timer")]
timer: bool,
}
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let ua = match var("HEALTHCHECKS_USERAGENT") { let ua = match var("HEALTHCHECKS_USERAGENT") {
Ok(f) => Some(f), Ok(f) => Some(f),
@ -20,48 +32,27 @@ fn main() -> anyhow::Result<()> {
.expect("HEALTHCHECKS_CHECK_ID must be set to run monitor"), .expect("HEALTHCHECKS_CHECK_ID must be set to run monitor"),
ua, ua,
}; };
let app = App::new("monitor") let opts = Opts::parse();
.version(env!("CARGO_PKG_VERSION")) let commands: Vec<Vec<String>> = if opts.command.len() == 1 {
.about("monitor runs the given command and reports execution result to https://healthchecks.io") opts.command
.setting(AppSettings::ColoredHelp) .get(0)
.setting(AppSettings::DeriveDisplayOrder)
.arg(
Arg::new("command")
.long("exec")
.short('X')
.min_values(1)
.allow_hyphen_values(true)
.value_terminator(";")
.value_name("cmd")
.required(true)
.about("Command to execute and monitor"),
)
.arg(
Arg::new("timer")
.long("timer")
.short('t')
.takes_value(false)
.about("Starts a timer before running the command"),
);
let matches = app.get_matches();
let cmds = matches
.values_of("command")
.expect("command must be passed")
.collect::<Vec<&str>>();
let commands: Vec<Vec<&str>> = if cmds.len() == 1 {
cmds.get(0)
.expect("This definitely has one command") .expect("This definitely has one command")
.split(';') .split(';')
.map(|c| c.split(' ').filter(|x| !x.is_empty()).collect()) .map(|c| {
c.split(' ')
.filter(|x| !x.is_empty())
.map(|x| x.to_string())
.collect()
})
.collect() .collect()
} else { } else {
vec![cmds] vec![opts.command]
}; };
let mut config = get_config(&settings.check_id)?; let mut config = get_config(&settings.check_id)?;
if let Some(user_agent) = settings.ua { if let Some(user_agent) = settings.ua {
config = config.set_user_agent(&user_agent) config = config.set_user_agent(&user_agent)
} }
if matches.is_present("timer") { if opts.timer {
config.start_timer(); config.start_timer();
} }
for cmds in commands { for cmds in commands {