diff --git a/src/config.rs b/src/config.rs index 0a32bf9..097fd28 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,7 +9,7 @@ pub struct Configuration { pub base_api: String, pub base_url: String, pub username: Option, - pub password: Option + pub password: Option, } impl Configuration { @@ -17,16 +17,14 @@ impl Configuration { let home_dir_env = env::var("HOME").unwrap(); let mut settings = config::Config::default(); let mut location: Vec = Vec::new(); - + // TODO: add condition for target os location.push("config.json".to_string()); location.push("/etc/gt/config.json".to_string()); location.push(format!("{}/.config/gt/config.json", home_dir_env)); for i in location { - settings.merge(File::with_name(&i) - .required(false)) - .unwrap(); + settings.merge(File::with_name(&i).required(false)).unwrap(); } let config = settings @@ -38,10 +36,7 @@ impl Configuration { } #[cfg(target_os = "linux")] -fn set_location_linux( - location: &mut Vec, - home: String - ) -> Vec { +fn set_location_linux(location: &mut Vec, home: String) -> Vec { location.push("config.json".to_string()); location.push("/etc/gt/config.json".to_string()); location.push(format!("{}/.config/gt/config.json", home)); @@ -50,20 +45,14 @@ fn set_location_linux( } #[cfg(target_os = "macos")] -fn set_location_macos( - location: &mut Vec, - home: String - ) -> Vec { +fn set_location_macos(location: &mut Vec, home: String) -> Vec { location.push("config.json".to_string()); location.to_vec() } #[cfg(target_os = "windows")] -fn set_location_windows( - location: &mut Vec, - home: String - ) -> Vec { +fn set_location_windows(location: &mut Vec, home: String) -> Vec { location.push("config.json".to_string()); location.to_vec() diff --git a/src/main.rs b/src/main.rs index 6f6bd6c..450a7d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,6 @@ use clap::ArgMatches; fn main() { let matches: ArgMatches = arg::get_args(); let config = crate::config::Configuration::new(); - let auth = request::Authentication::new(&config); let request = auth.request_chooser(config.clone(), matches); @@ -29,21 +28,21 @@ fn main() { } if repo_matches.is_present("delete") { - repo.delete_repo(&config, repo_matches); + repo.delete_repo(&request); } if repo_matches.is_present("fork") { - repo.fork_repo(&config, repo_matches) + repo.fork_repo(&request) } if repo_matches.is_present("search") { - repo.search_repo(&config, repo_matches) + repo.search_repo(&request) } - + if repo_matches.is_present("list") { - repo.list_repo(&config) + repo.list_repo(&request) } - }, + } ("issue", Some(issue_matches)) => { let issue = issue::Issue::new(); @@ -51,8 +50,7 @@ fn main() { if issue_matches.is_present("create") { issue.create_issue(&config, issue_matches); } - }, - _ => println!("Huh?") + } + _ => println!("Huh?"), } } - diff --git a/src/repo.rs b/src/repo.rs index 1a41b01..e81e8ec 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -1,11 +1,9 @@ use std::collections::HashMap; -use clap::ArgMatches; use colored::*; -use reqwest::{StatusCode, Client}; -use serde_derive::{Serialize, Deserialize}; +use reqwest::StatusCode; +use serde_derive::{Deserialize, Serialize}; -use crate::config::Configuration; use crate::request::Request; pub struct Repository; @@ -105,190 +103,196 @@ impl Repository { pub fn create_repo(&self, request: &Request) { let client = &request.client; - let arg_value = request.arg_value.subcommand().1.unwrap().value_of("create").unwrap(); + let arg_value = request + .arg_value + .subcommand() + .1 + .unwrap() + .value_of("create") + .unwrap(); let mut map: HashMap<&str, &str> = HashMap::new(); - let url = format!("{request}/user/repos?token={api_token}", + let url = format!( + "{request}/user/repos?token={api_token}", request = request.url.as_ref().unwrap(), - api_token = request - .authentication - .credentials - .1.as_ref() - .unwrap()); + api_token = request.authentication.credentials.1.as_ref().unwrap() + ); map.insert("name", arg_value); - map.insert("readme", arg_value); - map.insert("description", arg_value); - let response = client.post(url.as_str()) - .json(&map) - .send(); + let response = client.post(url.as_str()).json(&map).send(); match response { - Ok(mut repo) => { - match repo.status() { - StatusCode::CREATED => { - let deserialized: RepositoryResponse = repo.json().unwrap(); - println!("{}", "Repository successfully created!".green()); - println!("\tRepository name: {:0?}\n\tRepository owner: {:1?}\n\tRepository description: {:2?}", deserialized.name, deserialized.owner.unwrap().full_name, deserialized.description); - }, - StatusCode::CONFLICT => println!("{}", "Repository already exists!".red()), - StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository input validation failed!".red()), - _ => println!("Repository creation failed! HTTP status code: {}", repo.status().as_str()) + Ok(repo) => match repo.status() { + StatusCode::CREATED => { + let deserialized: RepositoryResponse = repo.json().unwrap(); + println!("{}", "Repository successfully created!".green()); + println!("\tRepository name: {:0}\n\tRepository owner: {:1}\n\tRepository description: {:2}", deserialized.name, deserialized.owner.unwrap().full_name, deserialized.description); } + StatusCode::CONFLICT => println!("{}", "Repository already exists!".red()), + StatusCode::UNPROCESSABLE_ENTITY => { + println!("{}", "Repository input validation failed!".red()) + } + _ => println!( + "Repository creation failed! HTTP status code: {}", + repo.status().as_str() + ), }, - Err(e) => panic!(e) + Err(e) => panic!("{}", e), } } - pub fn delete_repo(&self, config: &Configuration, arg: &ArgMatches) { - let client = Client::new(); - let arg_iter: Vec<&str> = arg.values_of("delete") + pub fn delete_repo(&self, request: &Request) { + let client = &request.client; + let arg_value: Vec<&str> = request + .arg_value + .subcommand() + .1 + .unwrap() + .values_of("delete") .unwrap() .collect(); + let url = format!( + r"{request}/repos/{owner}/{repo}?token={api_token}", + request = request.url.as_ref().unwrap(), + owner = request.authentication.credentials.0.as_ref().unwrap(), + repo = arg_value[1], + api_token = request.authentication.credentials.1.as_ref().unwrap(), + ); - let url = format!("{base_url}{base_api}/repos/{username}/{repo_name}?token={api_token}", - base_url = config.base_url, - base_api = config.base_api, - username = arg_iter[0], - repo_name = arg_iter[1], - api_token = config.api_token.as_ref().unwrap()); - - let response = client.delete(&url) - .send(); + let response = client.delete(&url).send(); match response { - Ok(repo) => { - match repo.status() { - StatusCode::NO_CONTENT => println!("{}", "Repository successfully deleted!".green()), - StatusCode::FORBIDDEN => println!("{}", "Forbidden to delete this repository!".red()), - StatusCode::NOT_FOUND => println!("{}", "Repository doesn't exist!".red()), - _ => println!("Repository deletion failed! HTTP status code: {}", repo.status().to_string()) - } + Ok(repo) => match repo.status() { + StatusCode::NO_CONTENT => println!("{}", "Respository successfully deleted!".green()), + StatusCode::FORBIDDEN => println!("{}", "Repository deletion forbidden!".red()), + _ => println!( + "Repository deletion failed! Does the repository exist? HTTP status code: {}", + repo.status().as_str() + ), }, - Err(e) => panic!(e) + Err(e) => panic!("{}", e), } } - pub fn fork_repo(&self, config: &Configuration, arg: &ArgMatches) { - let client = Client::new(); - let arg_item: Vec<&str> = arg.values_of("fork") + pub fn fork_repo(&self, request: &Request) { + let client = &request.client; + let arg_item: Vec<&str> = request + .arg_value + .subcommand() + .1 + .unwrap() + .values_of("fork") .unwrap() .collect(); let mut map: HashMap<&str, &str> = HashMap::new(); - let user = config.username - .clone() - .unwrap(); - + let user = request.authentication.credentials.0.as_ref().unwrap(); - let url = format!("{base_url}{base_api}/repos/{owner}/{repo}/forks?token={api_token}", - base_url = config.base_url, - base_api = config.base_api, + let url = format!( + "{request}/repos/{owner}/{repo}/forks?token={api_token}", + request = request.url.as_ref().unwrap(), owner = arg_item[0], repo = arg_item[1], - api_token = config.api_token.as_ref().unwrap()); + api_token = request.authentication.credentials.1.as_ref().unwrap(), + ); map.insert("name", user.as_str()); - let response = client.post(url.as_str()) - .json(&map) - .send(); + let response = client.post(url.as_str()).json(&map).send(); match response { - Ok(mut repo) => { - match repo.status() { - StatusCode::ACCEPTED => { - let deserialized: RepositoryResponse = repo.json().unwrap(); - println!("{}", "Repository forked successfully".green()); - println!("\tOriginal repository name: {:0?}\n\tOriginal repository owner: {:1?}\n\tForked repository name: {:2?}\n\tForked repository owner: {:3?}", deserialized.name, arg_item[0], deserialized.name, deserialized.owner.unwrap().full_name); - }, - StatusCode::INTERNAL_SERVER_ERROR => println!("{}", "Repository already forked!".red()), - StatusCode::FORBIDDEN => println!("{}", "Repository fork forbidden!".red()), - StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository fork input validation failed!".red()), - StatusCode::NOT_FOUND => println!("{}", "Repository not found!"), - _ => println!("Repository creation failed! HTTP status code: {}", repo.status().as_str()) + Ok(repo) => match repo.status() { + StatusCode::ACCEPTED => { + let deserialized: RepositoryResponse = repo.json().unwrap(); + println!("{}", "Repository forked successfully".green()); + println!("\tOriginal repository name: {:0?}\n\tOriginal repository owner: {:1?}\n\tForked repository name: {:2?}\n\tForked repository owner: {:3?}", deserialized.name, arg_item[0], deserialized.name, deserialized.owner.unwrap().full_name); } + StatusCode::INTERNAL_SERVER_ERROR => println!("{}", "Repository already forked!".red()), + StatusCode::FORBIDDEN => println!("{}", "Repository fork forbidden!".red()), + StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository fork input validation failed!".red()), + StatusCode::NOT_FOUND => println!("{}", "Repository not found!"), + _ => println!("Repository creation failed! HTTP status code: {}", repo.status().as_str()), }, - Err(e) => panic!(e) + Err(e) => panic!("{}", e), } } - pub fn search_repo(&self, config: &Configuration, arg: &ArgMatches) { - let client = Client::new(); - let arg_value = arg.value_of("search").unwrap(); - let url = format!("{base_url}{base_api}/repos/search?q={query}&token={api_token}", - base_url = config.base_url, - base_api = config.base_api, + pub fn search_repo(&self, request: &Request) { + let client = &request.client; + let arg_value = request + .arg_value + .subcommand() + .1 + .unwrap() + .value_of("search") + .unwrap(); + let url = format!( + "{request}/repos/search?q={query}&token={api_token}", + request = request.url.as_ref().unwrap(), query = arg_value, - api_token = config.api_token.as_ref().unwrap()); + api_token = request.authentication.credentials.1.as_ref().unwrap(), + ); - let response = client.get(url.as_str()) - .send(); + let response = client.get(url.as_str()).send(); match response { - Ok(mut repo) => { - match repo.status() { - StatusCode::OK => { - let deserialized: MultipleRepositories = repo.json() - .unwrap(); + Ok(repo) => match repo.status() { + StatusCode::OK => { + let deserialized: MultipleRepositories = repo.json().unwrap(); - match deserialized.data.len() != 0 { - true => { - println!("{}", "List of repositories found:".green()); + match deserialized.data.len() != 0 { + true => { + println!("{}", "List of repositories found:"); - for (i, data) in deserialized.data.iter().enumerate() { - println!("{}.\tRepository name: {:1?}\n\tRepository owner: {:2?}\n\tRepository description: {:3?}\n", i + 1, data.name, data.owner.as_ref().unwrap().full_name, data.description) - } - - println!("Total number of repositories indexed: {}", deserialized.data.iter().count()) - }, - false => println!("{}", "Repository searched doesn't exist!".red()) + for (i, data) in deserialized.data.iter().enumerate() { + println!("{}.\tRepository name: {:1}\n\tRepository owner: {:2}\n\tRepository description: {:3}\n", i + 1, data.name, data.owner.as_ref().unwrap().full_name, data.description) } - }, - StatusCode::NOT_FOUND => println!("{}", "Repository searched doesn't exist!".red()), - StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository input validation failed!".red()), - _ => println!("Repository search failed! HTTP status code: {}", repo.status().as_str()) + + println!("Total number of repositories indexed: {}", deserialized.data.iter().count()) + } + false => println!("{}", "Repository searched doesn't exist!".red()), + } } + StatusCode::NOT_FOUND => println!("{}", "Repository searched doesn't exist!".red()), + StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository input validation failed!".red()), + _ => println!("Repository search failed! HTTP status code: {}", repo.status().as_str()), }, - Err(e) => panic!(e) + Err(e) => panic!("{}", e), } } - pub fn list_repo(&self, config: &Configuration) { - let client = Client::new(); - let url = format!("{base_url}{base_api}/repos/search?token={api_token}", - base_url = config.base_url, - base_api = config.base_api, - api_token = config.api_token.as_ref().unwrap()); + pub fn list_repo(&self, request: &Request) { + let client = &request.client; + let url = format!( + "{request}/repos/search?token={api_token}", + request = request.url.as_ref().unwrap(), + api_token = request.authentication.credentials.1.as_ref().unwrap() + ); - let response = client.get(url.as_str()) - .send(); + let response = client.get(url.as_str()).send(); match response { - Ok(mut repo) => { - match repo.status() { - StatusCode::OK => { - let deserialized: MultipleRepositories = repo.json() - .unwrap(); + Ok(repo) => match repo.status() { + StatusCode::OK => { + let deserialized: MultipleRepositories = repo.json().unwrap(); - match deserialized.data.len() != 0 { + match deserialized.data.len() != 0 { true => { - println!("{}", "List of repositories found:".green()); + println!("{}", "List of repositories found:"); for (i, data) in deserialized.data.iter().enumerate() { - println!("{}.\tRepository name: {:1?}\n\tRepository owner: {:2?}\n\tRepository description: {:3?}\n", i + 1, data.name, data.owner.as_ref().unwrap().full_name, data.description) + println!("{}.\tRepository name: {:1}\n\tRepository owner: {:2}\n\tRepository description: {:3}\n", i + 1, data.name, data.owner.as_ref().unwrap().full_name, data.description) } println!("Total number of repositories indexed: {}", deserialized.data.iter().count()) }, false => println!("{}", "The authenticated user doesn't have any repositories. Why not create one?".yellow()) } - }, - StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository input validation failed!".red()), - _ => println!("Repository search failed! HTTP status code: {}", repo.status().as_str()) } + StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository input validation failed!".red()), + _ => println!("Repository search failed! HTTP status code: {}",repo.status().as_str()), }, - Err(e) => panic!(e) + Err(e) => panic!("{}", e), } - } + } } diff --git a/src/request.rs b/src/request.rs index e53c0a9..b0828b6 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use clap::ArgMatches; -use reqwest::Client; +use reqwest::blocking::Client; use crate::config::Configuration; @@ -16,78 +16,67 @@ pub struct Request<'a> { pub struct Authentication { pub basic: bool, pub api_token: bool, - pub credentials: (Option, Option) + pub credentials: (Option, Option), } impl<'a> Request<'a> { /// Public constructor for a request with a simple username and password pub fn with_basic_request( - config: Configuration, + config: Configuration, arg: ArgMatches, - auth: Authentication - ) -> Request { + auth: Authentication, + ) -> Request { Request { client: Client::new(), arg_value: arg, map: HashMap::new(), - url: Some(format!("{:?}{:?}", config.base_url, config.base_api)), + url: Some(format!("{}{}", config.base_url, config.base_api)), authentication: auth, } } /// Public constructor for a request with an API token pub fn with_api_request( - config: Configuration, + config: Configuration, arg: ArgMatches, - auth: Authentication - ) -> Request { + auth: Authentication, + ) -> Request { Request { client: Client::new(), arg_value: arg, map: HashMap::new(), - url: Some(format!("{:?}{:?}", config.base_url, config.base_api)), - authentication: auth, + url: Some(format!("{}{}", config.base_url, config.base_api)), + authentication: auth, } } } -impl Authentication{ +impl Authentication { /// Public constructor for getting authentication, provided by the configuration /// file. The most secure methods are checked first, filtering down to the least - /// secure methods. Currently, only two methods are supported: API token, and + /// secure methods. Currently, only two methods are supported: API token, and /// username/password combo. pub fn new(config: &Configuration) -> Authentication { // TODO: might be broken, haven't tested // this is horror code, I know it // match the damn thing // someone is going to take this and put it in r/badcode lol - let basic_auth = config - .password - .as_ref() - .unwrap() - .to_string(); + let basic_auth = config.password.as_ref().unwrap().to_string(); + + let api_auth = config.api_token.as_ref().unwrap().to_string(); - let api_auth = config - .api_token - .as_ref() - .unwrap() - .to_string(); - if api_auth.is_empty() { if !(basic_auth.is_empty()) { - Authentication::with_basic(config - .username - .as_ref() - .unwrap() - .to_string(), - basic_auth) + Authentication::with_basic( + config.username.as_ref().unwrap().to_string(), + basic_auth, + ) } else { panic!("Must have some form of authentication! Exiting..."); } } else { - Authentication::with_api_token(api_auth) - } - + Authentication::with_api_token(config.username.as_ref().unwrap().to_string(), api_auth) + } } /// Private constructor once the public constructor figures out what kind of authentication @@ -97,29 +86,25 @@ impl Authentication{ Authentication { basic: true, api_token: false, - credentials: (Some(user), Some(pass)) + credentials: (Some(user), Some(pass)), } } /// Private constructor once the public constructor figures out what kind of authentication /// is being used. This constructor uses the API token, a more secure way of authenticating /// instead of using the basic username and password. - fn with_api_token(api_token: String) -> Authentication { + fn with_api_token(user: String, api_token: String) -> Authentication { Authentication { basic: false, api_token: true, - credentials: (Some(api_token), None) + credentials: (Some(user), Some(api_token)), } } /// Public method that based on the what kind of authentication is being used, it can /// determine what kind of requesting method is going to be used. See the Request /// structure for more details. - pub fn request_chooser( - self, - config: Configuration, - arg: ArgMatches<'static>, - ) -> Request { + pub fn request_chooser(self, config: Configuration, arg: ArgMatches<'static>) -> Request { if let true = self.api_token { Request::with_api_request(config, arg.to_owned(), self) } else { diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 0000000..99d3037 --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,5 @@ +#[test] +fn test_add() { + let x = 2 + 2; + assert_eq!(x, 4); +} \ No newline at end of file