From f834353e02586110d922f7468e3d6b57b51c2de3 Mon Sep 17 00:00:00 2001 From: Herpiko Dwi Aguno Date: Sat, 4 Feb 2023 17:23:21 +0700 Subject: [PATCH] Tidy up. Use struct and yaml to maintain configuration. --- Cargo.lock | 39 +++++++++++ Cargo.toml | 3 + src/main.rs | 182 ++++++++++++++++++++++++++++++---------------------- 3 files changed, 148 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63f5048..8f8eb24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,8 +166,11 @@ version = "0.1.0" dependencies = [ "clap", "dirs", + "question", "reqwest", + "serde", "serde_json", + "serde_yaml", ] [[package]] @@ -556,6 +559,12 @@ version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "lock_api" version = "0.1.5" @@ -848,6 +857,12 @@ dependencies = [ "url", ] +[[package]] +name = "question" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbb3ede7a8f9a8ab89e714637f2cf40001b58f21340d4242b2f11533e65fa8d" + [[package]] name = "quote" version = "0.6.12" @@ -1147,6 +1162,9 @@ name = "serde" version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a72e9b96fa45ce22a4bc23da3858dfccfd60acd28a25bcd328a98fdd6bea43fd" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" @@ -1182,6 +1200,18 @@ dependencies = [ "url", ] +[[package]] +name = "serde_yaml" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039ba818c784248423789eec090aab9fb566c7b94d6ebbfa1814a9fd52c8afb2" +dependencies = [ + "dtoa", + "linked-hash-map", + "serde", + "yaml-rust", +] + [[package]] name = "siphasher" version = "0.2.3" @@ -1594,3 +1624,12 @@ dependencies = [ "winapi 0.2.8", "winapi-build", ] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/Cargo.toml b/Cargo.toml index ef0085c..9c57365 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,6 @@ clap = "2.26.0" dirs = "1.0.5" reqwest = "0.9.15" serde_json = "1.0.39" +serde = { version = "1.0", features = ["derive"] } +serde_yaml = "0.8" +question = "0.2.2" diff --git a/src/main.rs b/src/main.rs index d9fbca2..739bfb5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,49 @@ extern crate clap; extern crate dirs; extern crate reqwest; -#[macro_use] extern crate serde_json; +#[macro_use] +extern crate serde_json; use clap::{App, Arg, SubCommand}; +use serde::{Deserialize, Serialize}; +use serde_yaml::{self}; use std::fs; -use std::fs::File; -use std::io::Write; use std::num::ParseIntError; +use std::path::Path; use std::process; -use url::{Url, ParseError}; + +extern crate question; +use question::{Answer, Question}; + + +const CONFIG_FILE_PATH: &str = ".irgsh/irgsh.yaml"; + +#[derive(Debug, Serialize, Deserialize)] +struct Config { + chief_url: String, + maintainer_key: String, + config_file_path: String, +} + +impl Config { + fn set(&self) { + let f = std::fs::OpenOptions::new() + .write(true) + .create(true) + .open(&self.config_file_path) + .expect("Couldn't write file"); + serde_yaml::to_writer(f, &self).unwrap(); + } + fn load(&self) -> Config { + let f = std::fs::File::open(&self.config_file_path).expect("Could not open file."); + let loaded: Config = serde_yaml::from_reader(f).expect("Could not read values."); + return loaded; + } +} fn graceful_exit(message: String, exit_code: i32) -> Result { - println!("{}", message); - process::exit(exit_code); + println!("{}", message); + process::exit(exit_code); } fn main() -> Result<(), ParseIntError> { @@ -80,65 +110,87 @@ fn main() -> Result<(), ParseIntError> { .index(1))) .get_matches(); + // Prepare path let home_dir_path = dirs::home_dir().unwrap(); - let mut config_file = home_dir_path.into_os_string().into_string().unwrap(); - + let home_dir_path = home_dir_path.into_os_string().into_string().unwrap(); + let mut config_file_path = home_dir_path.clone(); + config_file_path.push_str("/"); + config_file_path.push_str(&CONFIG_FILE_PATH); + + // Global config, could be reused anywhere under subcommand scopes bellow + let mut config = Config { + config_file_path: config_file_path.to_string(), + chief_url: "".to_string(), + maintainer_key: "".to_string(), + }; if let Some(matches) = matches.subcommand_matches("init") { - let mut path_str = "/.irgsh"; - config_file.push_str(&path_str); - fs::create_dir(&config_file).ok(); - path_str = "/IRGSH_CHIEF_ADDRESS"; - config_file.push_str(&path_str); - // TOOD validate URL + // Retrieve the arguments/params let url = matches.value_of("chief").unwrap(); let key = matches.value_of("key").unwrap(); - println!("{url}"); - println!("{key}"); - let mut f = File::create(&config_file).expect("Unable to create file"); - f.write_all(url.as_bytes()).expect("Unable to write data"); + config.chief_url = url.to_string(); + config.maintainer_key = key.to_string(); + + if Path::new(&config.config_file_path).exists() { + let loaded = config.load(); + println!("Current config file is already exist with this content:"); + println!("{:?}", loaded); + let answer = Question::new("Do you want to continue to override this with your new config?") + .default(Answer::YES) + .show_defaults() + .confirm(); + if answer != Answer::YES { + return graceful_exit("".to_string(), 0); + } + } + + let mut config_dir = home_dir_path.clone(); + config_dir.push_str("/.irgsh"); + fs::create_dir(&config_dir).ok(); + + // Write the config to file + config.set(); println!( - "Successfully sets the chief address to {}. Now you can use irgsh-cli.", - matches.value_of("chief").unwrap() + "Successfully sets the chief address to {}. Now you can use irgsh-cli. Happy Hacking", + url.to_string() ); return Ok(()); } - config_file.push_str("/.irgsh/IRGSH_CHIEF_ADDRESS"); - let chief_url_result = fs::read_to_string(&config_file); - let chief_url = match chief_url_result { - Ok(url) => url, - Err(error) => "Error: unable to read config file. Please initialize the irgsh-cli first. See --help for further information.".to_string(), - }; - - if chief_url.contains("Error") { - return graceful_exit(chief_url, 1); + if !Path::new(&config.config_file_path).exists() { + let err_message: &str = "Error: unable to read config file. Please initialize the irgsh-cli first. See --help for further information."; + return graceful_exit(err_message.to_string(), 1); } if let Some(matches) = matches.subcommand_matches("submit") { - println!("Chief : {}", chief_url); - if matches.value_of("source").unwrap().chars().count() > 0 { - println!("Source URL : {}", matches.value_of("source").unwrap()); - } - if matches.value_of("package").unwrap().chars().count() > 0 { - println!("Package URL : {}", matches.value_of("package").unwrap()); - } - let mut chief_url = fs::read_to_string(&config_file).expect("Unable to read config file. Please initialize the irgsh-cli first. See --help for further information."); + // Retrieve the arguments/params + let source = matches.value_of("source").unwrap(); + let package = matches.value_of("package").unwrap(); + + config.load(); + + println!("Chief : {}", &config.chief_url); + if matches.value_of("source").unwrap().chars().count() > 0 { + println!("Source URL : {}", source); + } + if matches.value_of("package").unwrap().chars().count() > 0 { + println!("Package URL : {}", package); + } + let mut chief_url = config.chief_url.clone(); chief_url.push_str("/api/v1/submit"); println!("Submit URL : {}", chief_url); - let payload: serde_json::Value = json!({ - "sourceUrl": matches.value_of("source").unwrap(), - "packageUrl": matches.value_of("package").unwrap() - }); + let payload: serde_json::Value = json!({ + "sourceUrl": source, + "packageUrl": package + }); - let _result: serde_json::Value = match post(chief_url, payload) { - Ok(result) => result, - Err(_e) => return Ok(()) - }; + let _result: serde_json::Value = match post(chief_url, payload) { + Ok(result) => result, + Err(_e) => return Ok(()), + }; return Ok(()); - } else if let Some(matches) = matches.subcommand_matches("status") { println!( "Status of PipelineID: {}", @@ -158,35 +210,13 @@ fn main() -> Result<(), ParseIntError> { } } - fn post(url: String, json_payload: serde_json::Value) -> Result { - let echo_json: serde_json::Value = reqwest::Client::new() - .post(&url) - .json(&json_payload - ) - .send()? - .json()?; - - println!("{:#?}", echo_json); - return Ok(echo_json) -} - -fn handler(e: reqwest::Error) { - if e.is_http() { - match e.url() { - None => println!("No Url given"), - Some(url) => println!("Problem making request to: {}", url), - } - } - // Inspect the internal error and output it - if e.is_serialization() { - let serde_error = match e.get_ref() { - None => return, - Some(err) => err, - }; - println!("problem parsing information {}", serde_error); - } - if e.is_redirect() { - println!("server redirecting too many times or making loop"); - } + let echo_json: serde_json::Value = reqwest::Client::new() + .post(&url) + .json(&json_payload) + .send()? + .json()?; + + println!("{:#?}", echo_json); + return Ok(echo_json); }