Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Format code & Used pattern matching to match subcommands
Browse files Browse the repository at this point in the history
Added argsElseHelp flag

formatted code

Fixed typo
vedangj044 committed Mar 18, 2021
1 parent 1be0ae3 commit 39db801
Showing 6 changed files with 246 additions and 207 deletions.
37 changes: 21 additions & 16 deletions src/apps.rs
Original file line number Diff line number Diff line change
@@ -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<Response> {
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<Response, reqw
let client = Client::new();
let url = format!("{}api/v1/apps/{}", url, app);

client.put(&url)
client
.put(&url)
.header(reqwest::header::CONTENT_TYPE, "application/json")
.body(data.to_string())
.send()
}
}
179 changes: 97 additions & 82 deletions src/arguments.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::util;

use clap::{Arg, App, SubCommand, ArgMatches};
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use std::convert::AsRef;
use strum_macros::{AsRefStr, EnumString};

@@ -30,93 +30,108 @@ pub enum Parameters {
}

pub fn parse_arguments() -> 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 <jbtrystram@redhat.com>")
.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 <jbtrystram@redhat.com>")
.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()
}
.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()
}
15 changes: 4 additions & 11 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -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,17 +9,14 @@ pub struct Config {
}

pub fn load_config_file(path: Option<&str>) -> Result<Config> {

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);

let file = File::open(path)?;
let config: Config = serde_json::from_reader(file)?;
Ok(config)
}
}
45 changes: 28 additions & 17 deletions src/devices.rs
Original file line number Diff line number Diff line change
@@ -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<Response> {
client.get(&url).send().context("Can't get device.")
}

fn put(url: &Url, app: &AppId, device_id: &DeviceId, data: serde_json::Value) -> Result<Response, reqwest::Error> {
fn put(
url: &Url,
app: &AppId,
device_id: &DeviceId,
data: serde_json::Value,
) -> Result<Response, reqwest::Error> {
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()
}
}
108 changes: 65 additions & 43 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -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<String>;
@@ -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(())
}
}
69 changes: 31 additions & 38 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -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> {
Url::parse(url.unwrap()).context(format!("URL args: \'{}\' is not valid", url.unwrap()))
}

pub fn json_parse(data: Option<&str>) -> Result<Value> {
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<Value> {

//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<Value> {
}

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);
}
}

0 comments on commit 39db801

Please sign in to comment.