From 39db801b27f8b96f96af049372f0c01099acbdcb Mon Sep 17 00:00:00 2001 From: vedang Date: Wed, 17 Mar 2021 10:30:21 +0530 Subject: [PATCH] Format code & Used pattern matching to match subcommands Added argsElseHelp flag formatted code Fixed typo --- src/apps.rs | 37 +++++----- src/arguments.rs | 179 +++++++++++++++++++++++++---------------------- src/config.rs | 15 ++-- src/devices.rs | 45 +++++++----- src/main.rs | 108 ++++++++++++++++------------ src/util.rs | 69 ++++++++---------- 6 files changed, 246 insertions(+), 207 deletions(-) diff --git a/src/apps.rs b/src/apps.rs index c5dcdf5..4b95c54 100644 --- a/src/apps.rs +++ b/src/apps.rs @@ -1,8 +1,8 @@ -use crate::{AppId, Verbs, util}; +use crate::{util, AppId, Verbs}; +use anyhow::{Context, Result}; use reqwest::blocking::{Client, Response}; use reqwest::{StatusCode, Url}; use serde_json::json; -use anyhow::{Context, Result}; pub fn create(url: &Url, app: &AppId, data: serde_json::Value) -> Result<()> { let client = Client::new(); @@ -16,10 +16,12 @@ pub fn create(url: &Url, app: &AppId, data: serde_json::Value) -> Result<()> { } }); - let res = client.post(&url) + let res = client + .post(&url) .header(reqwest::header::CONTENT_TYPE, "application/json") .body(body.to_string()) - .send().context("Can't create app.")?; + .send() + .context("Can't create app.")?; util::print_result(res, format!("App {}", app), Verbs::create); Ok(()) @@ -44,20 +46,22 @@ pub fn edit(url: &Url, app: &AppId) { //read app data let res = get(url, app); match res { - Ok(r) => { - match r.status() { - StatusCode::OK => { - let body = r.text().unwrap_or("{}".to_string()); - let insert = util::editor(body).unwrap(); - util::print_result(put(url, app, insert).unwrap(), format!("App {}", app), Verbs::edit) - }, - e => println!("Error : could not retrieve app: {}", e) + Ok(r) => match r.status() { + StatusCode::OK => { + let body = r.text().unwrap_or("{}".to_string()); + let insert = util::editor(body).unwrap(); + util::print_result( + put(url, app, insert).unwrap(), + format!("App {}", app), + Verbs::edit, + ) } - }, Err(e) => println!("Error : could not retrieve app: {}", e) + e => println!("Error : could not retrieve app: {}", e), + }, + Err(e) => println!("Error : could not retrieve app: {}", e), } } - fn get(url: &Url, app: &AppId) -> Result { let client = Client::new(); let url = format!("{}api/v1/apps/{}", url, app); @@ -68,8 +72,9 @@ fn put(url: &Url, app: &AppId, data: serde_json::Value) -> Result ArgMatches<'static> { + let resource_id_arg = Arg::with_name(Parameters::id.as_ref()) + .required(true) + .help("The unique id of the resource."); -let resource_id_arg = Arg::with_name(Parameters::id.as_ref()) - .required(true) - .help("The unique id of the resource."); + let url_arg = Arg::with_name(Parameters::url.as_ref()) + .long(Parameters::url.as_ref()) + .takes_value(true) + .help("The url of the registry endpoint"); -let url_arg = Arg::with_name(Parameters::url.as_ref()) - .long(Parameters::url.as_ref()) - .takes_value(true) - .help("The url of the registry endpoint"); + let app_id_arg = Arg::with_name(Resources::app.as_ref()) + .required(true) + .long(Resources::app.as_ref()) + .takes_value(true) + .help("The app owning the device."); -let app_id_arg = Arg::with_name(Resources::app.as_ref()) - .long(Resources::app.as_ref()) - .takes_value(true) - .help("The app owning the device."); + let data_arg = Arg::with_name(Parameters::data.as_ref()) + .short("d") + .long(Parameters::data.as_ref()) + .takes_value(true) + .help("The data for the resource."); -let data_arg = Arg::with_name(Parameters::data.as_ref()) - .short("d") - .long(Parameters::data.as_ref()) - .takes_value(true) - .help("The data for the resource."); + let config_file_arg = Arg::with_name(Parameters::config.as_ref()) + .long(Parameters::config.as_ref()) + .takes_value(true) + .conflicts_with(Parameters::url.as_ref()) + .help("Path to the drgconfig file. Defaults to ~/.drgconfig"); -let config_file_arg = Arg::with_name(Parameters::config.as_ref()) - .long(Parameters::config.as_ref()) - .takes_value(true) - .conflicts_with(Parameters::url.as_ref()) - .help("Path to the drgconfig file. Defaults to ~/.drgconfig"); - - -App::new("Drogue Command Line Tool") - .version(util::VERSION) - .author("Jb Trystram ") - .about("Allows to manage drogue apps and devices in a drogue-cloud instance") - .arg(config_file_arg) - .arg(url_arg) - .subcommand(SubCommand::with_name(Verbs::create.as_ref()) - .alias("add") - .about("create a resource in the drogue-cloud registry") - .subcommand( - SubCommand::with_name(Resources::device.as_ref()) - .about("create a device.") - .arg(resource_id_arg.clone()) - .arg(app_id_arg.clone()) - .arg(data_arg.clone()) - ).subcommand(SubCommand::with_name(Resources::app.as_ref()) - .about("create an app.") - .arg(resource_id_arg.clone()) - .arg(data_arg.clone()) - ) - ).subcommand( - SubCommand::with_name(Verbs::delete.as_ref()) - .alias("remove") - .about("delete a resource in the drogue-cloud registry") - .subcommand( - SubCommand::with_name(Resources::device.as_ref()) - .about("delete a device.") - .arg(resource_id_arg.clone()) - .arg(app_id_arg.clone()) - ).subcommand(SubCommand::with_name(Resources::app.as_ref()) - .about("create an app.") - .arg(resource_id_arg.clone()) - ) - ).subcommand( - SubCommand::with_name(Verbs::get.as_ref()) - .about("Read a resource from the drogue-cloud registry") + App::new("Drogue Command Line Tool") + .version(util::VERSION) + .author("Jb Trystram ") + .about("Allows to manage drogue apps and devices in a drogue-cloud instance") + .arg(config_file_arg) + .arg(url_arg) + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name(Verbs::create.as_ref()) + .alias("add") + .about("create a resource in the drogue-cloud registry") + .setting(AppSettings::ArgRequiredElseHelp) + .subcommand( + SubCommand::with_name(Resources::device.as_ref()) + .about("create a device.") + .arg(resource_id_arg.clone()) + .arg(app_id_arg.clone()) + .arg(data_arg.clone()), + ) + .subcommand( + SubCommand::with_name(Resources::app.as_ref()) + .about("create an app.") + .arg(resource_id_arg.clone()) + .arg(data_arg.clone()), + ), + ) .subcommand( - SubCommand::with_name(Resources::device.as_ref()) - .about("Retrieve a device data.") - .arg(resource_id_arg.clone()) - .arg(app_id_arg.clone()) - ).subcommand(SubCommand::with_name(Resources::app.as_ref()) - .about("retrieve an app data.") - .arg(resource_id_arg.clone()) + SubCommand::with_name(Verbs::delete.as_ref()) + .alias("remove") + .about("delete a resource in the drogue-cloud registry") + .setting(AppSettings::ArgRequiredElseHelp) + .subcommand( + SubCommand::with_name(Resources::device.as_ref()) + .about("delete a device.") + .arg(resource_id_arg.clone()) + .arg(app_id_arg.clone()), + ) + .subcommand( + SubCommand::with_name(Resources::app.as_ref()) + .about("create an app.") + .arg(resource_id_arg.clone()), + ), ) - ).subcommand( - SubCommand::with_name(Verbs::edit.as_ref()) - .about("Edit a resource from the drogue-cloud registry") .subcommand( - SubCommand::with_name(Resources::device.as_ref()) - .about("Edit a device data.") - .arg(resource_id_arg.clone()) - .arg(app_id_arg.clone()) - ).subcommand(SubCommand::with_name(Resources::app.as_ref()) - .about("Edit an app data.") - .arg(resource_id_arg.clone()) + SubCommand::with_name(Verbs::get.as_ref()) + .about("Read a resource from the drogue-cloud registry") + .setting(AppSettings::ArgRequiredElseHelp) + .subcommand( + SubCommand::with_name(Resources::device.as_ref()) + .about("Retrieve a device data.") + .arg(resource_id_arg.clone()) + .arg(app_id_arg.clone()), + ) + .subcommand( + SubCommand::with_name(Resources::app.as_ref()) + .about("retrieve an app data.") + .arg(resource_id_arg.clone()), + ), ) - ).subcommand( - SubCommand::with_name("version") - .about("Print version information.") - ).get_matches() -} \ No newline at end of file + .subcommand( + SubCommand::with_name(Verbs::edit.as_ref()) + .about("Edit a resource from the drogue-cloud registry") + .setting(AppSettings::ArgRequiredElseHelp) + .subcommand( + SubCommand::with_name(Resources::device.as_ref()) + .about("Edit a device data.") + .arg(resource_id_arg.clone()) + .arg(app_id_arg.clone()), + ) + .subcommand( + SubCommand::with_name(Resources::app.as_ref()) + .about("Edit an app data.") + .arg(resource_id_arg.clone()), + ), + ) + .subcommand(SubCommand::with_name("version").about("Print version information.")) + .get_matches() +} diff --git a/src/config.rs b/src/config.rs index 1610901..c4197c7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,10 +1,6 @@ -use anyhow::{Result}; +use anyhow::Result; use serde::{Deserialize, Serialize}; -use std::{ - env::var, - fs::File, -}; - +use std::{env::var, fs::File}; #[derive(Serialize, Deserialize, Debug)] pub struct Config { @@ -13,12 +9,9 @@ pub struct Config { } pub fn load_config_file(path: Option<&str>) -> Result { - let path = match path { Some(p) => p.to_string(), - None => { - var("DRGCFG").unwrap_or(format!("{}/.drgconfig.json", var("HOME")?)) - } + None => var("DRGCFG").unwrap_or(format!("{}/.drgconfig.json", var("HOME")?)), }; //todo verbose option println!("Loading config file: {}", path); @@ -26,4 +19,4 @@ pub fn load_config_file(path: Option<&str>) -> Result { let file = File::open(path)?; let config: Config = serde_json::from_reader(file)?; Ok(config) -} \ No newline at end of file +} diff --git a/src/devices.rs b/src/devices.rs index c46cbb3..481b365 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -1,9 +1,9 @@ -use crate::{AppId, Verbs, DeviceId, util}; +use crate::{util, AppId, DeviceId, Verbs}; +use anyhow::{Context, Result}; use reqwest::blocking::Client; +use reqwest::blocking::Response; use reqwest::{StatusCode, Url}; use serde_json::json; -use reqwest::blocking::Response; -use anyhow::{Result, Context}; const API_BASE: &str = "api/v1/apps"; @@ -33,10 +33,12 @@ pub fn create(url: &Url, id: &DeviceId, data: serde_json::Value, app_id: &AppId) }, "spec": data }); - let res = client.post(&url) + let res = client + .post(&url) .header(reqwest::header::CONTENT_TYPE, "application/json") .body(body.to_string()) - .send().context("Can't create device.")?; + .send() + .context("Can't create device.")?; util::print_result(res, format!("Device {}", id), Verbs::create); Ok(()) @@ -46,16 +48,19 @@ pub fn edit(url: &Url, app: &AppId, device_id: &DeviceId) { //read device data let res = get(url, app, device_id); match res { - Ok(r) => { - match r.status() { - StatusCode::OK => { - let body = r.text().unwrap_or("{}".to_string()); - let insert = util::editor(body).unwrap(); - util::print_result(put(url, app, device_id, insert).unwrap(), format!("Device {}", device_id), Verbs::edit) - }, - e => println!("Error : could not retrieve device: {}", e) + Ok(r) => match r.status() { + StatusCode::OK => { + let body = r.text().unwrap_or("{}".to_string()); + let insert = util::editor(body).unwrap(); + util::print_result( + put(url, app, device_id, insert).unwrap(), + format!("Device {}", device_id), + Verbs::edit, + ) } - }, Err(e) => println!("Error : could not retrieve device: {}", e) + e => println!("Error : could not retrieve device: {}", e), + }, + Err(e) => println!("Error : could not retrieve device: {}", e), } } @@ -66,12 +71,18 @@ fn get(url: &Url, app: &AppId, device_id: &DeviceId) -> Result { client.get(&url).send().context("Can't get device.") } -fn put(url: &Url, app: &AppId, device_id: &DeviceId, data: serde_json::Value) -> Result { +fn put( + url: &Url, + app: &AppId, + device_id: &DeviceId, + data: serde_json::Value, +) -> Result { let client = Client::new(); let url = format!("{}{}/{}/devices/{}", url, API_BASE, app, device_id); - client.put(&url) + client + .put(&url) .header(reqwest::header::CONTENT_TYPE, "application/json") .body(data.to_string()) .send() -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 5e9a138..2ec9fcb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,9 +4,9 @@ mod config; mod devices; mod util; -use arguments::{Parameters, Verbs, Resources}; +use arguments::{Parameters, Resources, Verbs}; -use anyhow::{Result, Context}; +use anyhow::{Context, Result}; use std::str::FromStr; type AppId = str; @@ -19,14 +19,6 @@ fn main() -> Result<()> { util::print_version(); } - // TODO : unsafe unwraps !!!! - let (cmd_name, cmd) = matches.subcommand(); - //deserialize the command into enum to take advantage of rust exhaustive match - let verb = Verbs::from_str(cmd_name).unwrap(); - let (sub_cmd_name, sub_cmd) = cmd.unwrap().subcommand(); - let resource = Resources::from_str(sub_cmd_name).unwrap(); - let id = sub_cmd.unwrap().value_of(Parameters::id).unwrap(); - let url; //todo default app is not used let _default_app: Option; @@ -41,45 +33,75 @@ fn main() -> Result<()> { _default_app = conf.default_app; } - match verb { - Verbs::create => { - let data = util::json_parse(sub_cmd.unwrap().value_of(Parameters::data))?; - match resource { - Resources::app => apps::create(&url, id, data)?, - Resources::device => { - let app_id = sub_cmd.unwrap().value_of(Resources::app).unwrap(); - devices::create(&url, id, data, app_id)? + match matches.subcommand() { + (cmd_name, sub_cmd) => { + let verb = Verbs::from_str(cmd_name); + let cmd = sub_cmd.unwrap(); + + match verb? { + Verbs::create => match cmd.subcommand() { + (res, command) => { + let data = util::json_parse(command.unwrap().value_of(Parameters::data))?; + let id = command.unwrap().value_of(Parameters::id).unwrap(); + + let resource = Resources::from_str(res); + + match resource? { + Resources::app => apps::create(&url, id, data)?, + Resources::device => { + let app_id = command.unwrap().value_of(Resources::app).unwrap(); + devices::create(&url, id, data, app_id)? + } + } + } }, - } - } - Verbs::delete => { - match resource { - Resources::app => apps::delete(&url, id)?, - Resources::device => { - let app_id = sub_cmd.unwrap().value_of(Resources::app).unwrap(); - devices::delete(&url, app_id, id)? + Verbs::delete => match cmd.subcommand() { + (res, command) => { + let id = command.unwrap().value_of(Parameters::id).unwrap(); + let resource = Resources::from_str(res); + + match resource? { + Resources::app => apps::delete(&url, id)?, + Resources::device => { + let app_id = command.unwrap().value_of(Resources::app).unwrap(); + devices::delete(&url, app_id, id)? + } + } + } }, - } - } - Verbs::edit => { - match resource { - Resources::app => apps::edit(&url, id), - Resources::device => { - let app_id = sub_cmd.unwrap().value_of(Resources::app).unwrap(); - devices::edit(&url, app_id, id) + Verbs::edit => match cmd.subcommand() { + (res, command) => { + let id = command.unwrap().value_of(Parameters::id).unwrap(); + + let resource = Resources::from_str(res); + + match resource? { + Resources::app => apps::edit(&url, id), + Resources::device => { + let app_id = command.unwrap().value_of(Resources::app).unwrap(); + devices::edit(&url, app_id, id) + } + } + } }, - } - } - Verbs::get => { - match resource { - Resources::app => apps::read(&url, id)?, - Resources::device => { - let app_id = sub_cmd.unwrap().value_of(Resources::app).unwrap(); - devices::read(&url, app_id, id)? + Verbs::get => match cmd.subcommand() { + (res, command) => { + let id = command.unwrap().value_of(Parameters::id).unwrap(); + + let resource = Resources::from_str(res); + + match resource? { + Resources::app => apps::read(&url, id)?, + Resources::device => { + let app_id = command.unwrap().value_of(Resources::app).unwrap(); + devices::read(&url, app_id, id)? + } + } + } }, } } } Ok(()) -} \ No newline at end of file +} diff --git a/src/util.rs b/src/util.rs index adf7a91..86d383b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,72 +1,65 @@ -use crate::{Verbs}; -use anyhow::{Result, Context}; +use crate::Verbs; +use anyhow::{Context, Result}; use reqwest::blocking::Response; use reqwest::{StatusCode, Url}; -use serde_json::{Value, from_str}; +use serde_json::{from_str, Value}; +use std::process::exit; use std::{ env::var, - io::{Write, Read}, + io::{Read, Write}, process::Command, }; use tempfile::NamedTempFile; -use std::process::exit; pub const VERSION: &str = "0.1-beta1"; pub const COMPATIBLE_DROGUE_VERSION: &str = "0.3.0"; pub fn print_result(r: Response, resource_name: String, op: Verbs) { match op { - Verbs::create => { - match r.status() { - StatusCode::CREATED => println!("{} created.", resource_name), - r => println!("Error : {}", r), - } - }, Verbs::delete => { - match r.status() { - StatusCode::NO_CONTENT => println!("{} deleted.", resource_name), - r => println!("Error : {}", r), - } - }, Verbs::get => { - match r.status() { - StatusCode::OK => println!("{}", r.text().expect("Empty response")), - r => println!("Error : {}", r), - } - }, Verbs::edit => { - match r.status() { - StatusCode::NO_CONTENT => println!("{} edited.", resource_name), - r => println!("Error : {}", r), - } - } + Verbs::create => match r.status() { + StatusCode::CREATED => println!("{} created.", resource_name), + r => println!("Error : {}", r), + }, + Verbs::delete => match r.status() { + StatusCode::NO_CONTENT => println!("{} deleted.", resource_name), + r => println!("Error : {}", r), + }, + Verbs::get => match r.status() { + StatusCode::OK => println!("{}", r.text().expect("Empty response")), + r => println!("Error : {}", r), + }, + Verbs::edit => match r.status() { + StatusCode::NO_CONTENT => println!("{} edited.", resource_name), + r => println!("Error : {}", r), + }, } - } - - pub fn url_validation(url: Option<&str>) -> Result { Url::parse(url.unwrap()).context(format!("URL args: \'{}\' is not valid", url.unwrap())) } pub fn json_parse(data: Option<&str>) -> Result { - from_str(data.unwrap_or("{}")).context(format!("Can't parse data args: \'{}\' into json", data.unwrap())) + from_str(data.unwrap_or("{}")).context(format!( + "Can't parse data args: \'{}\' into json", + data.unwrap_or("") + )) } - pub fn editor(original: String) -> Result { - //TODO : that would not work on windows ! let editor = var("EDITOR").unwrap_or("vi".to_string()); let file = NamedTempFile::new()?; //the handler needs to be kept to reopen the file later. - let mut file2= file.reopen()?; + let mut file2 = file.reopen()?; // Write the original data to the file. file.as_file().write_all(original.as_bytes())?; Command::new(editor) - .arg(file.path()) - .status() - .expect("Could not open current data in editor."); + .arg(file.path()) + .status() + .expect("Could not open current data in editor."); // Read the data using the second handle. let mut buf = String::new(); @@ -76,11 +69,11 @@ pub fn editor(original: String) -> Result { } pub fn print_version() { - //todo add git hash and build date to version output ? + //todo add git hash and build date to version output ? println!("Client Version: {}", VERSION); println!("Compatible Server Version: {}", COMPATIBLE_DROGUE_VERSION); //todo connect to server and retrieve version. exit(0); -} \ No newline at end of file +}