From ff303bbbf85c26fb4adf70e29b8e2e5c7a40efde Mon Sep 17 00:00:00 2001 From: Ivan Popugaev Date: Sun, 13 Mar 2022 15:05:17 +0300 Subject: [PATCH] ready --- src/cmd/search.rs | 134 ++++++++++++++------------------- src/cmd/search/commands.rs | 35 +++++++++ src/cmd/search/groups.rs | 42 +++++++++++ src/cmd/search/groups_cmd.rs | 64 ---------------- src/cmd/search/projects.rs | 42 +++++++++++ src/cmd/search/projects_cmd.rs | 64 ---------------- src/cmd/search/users.rs | 42 +++++++++++ src/cmd/search/users_cmd.rs | 57 -------------- src/main.rs | 82 +++++++++----------- 9 files changed, 254 insertions(+), 308 deletions(-) create mode 100644 src/cmd/search/commands.rs create mode 100644 src/cmd/search/groups.rs delete mode 100644 src/cmd/search/groups_cmd.rs create mode 100644 src/cmd/search/projects.rs delete mode 100644 src/cmd/search/projects_cmd.rs create mode 100644 src/cmd/search/users.rs delete mode 100644 src/cmd/search/users_cmd.rs diff --git a/src/cmd/search.rs b/src/cmd/search.rs index 6940a22..4b9e165 100644 --- a/src/cmd/search.rs +++ b/src/cmd/search.rs @@ -1,94 +1,74 @@ -mod groups_cmd; -mod projects_cmd; -mod users_cmd; +pub(crate) mod commands; +mod groups; +mod projects; +mod users; -use std::io::{Error, ErrorKind}; - -use clap::{ArgMatches, Command}; +use std::{ + collections::HashMap, + fmt::{self, Display, Formatter}, + io::{Error, Result}, + str::FromStr, +}; use gitlab::Gitlab; -use crate::{ - args::{gitlab_token::ArgGitlabToken, gitlab_url::ArgGitlabUrl, Args}, - cmd::Cmd, -}; +use self::{groups::Groups, projects::Projects, users::Users}; -/// Register search cmd -pub(crate) fn add_search_cmd() -> Command<'static> { - return Command::new("search") - .aliases(&["s", "find"]) - .about("Search for GitLab entities") - .arg(ArgGitlabToken::add()) - .arg(ArgGitlabUrl::add()) - .arg_required_else_help(true) - .subcommand(projects_cmd::find_projects()) - .subcommand(users_cmd::find_users()) - .subcommand(groups_cmd::find_groups()); +pub(crate) trait SearchEntity { + fn search(&self, query: &str) -> Result<()>; } -pub(crate) struct SearchCmd<'a> { - // search_string: String, - search_sub: Option<(&'a str, &'a ArgMatches)>, - gitlab_client: Gitlab, +pub(crate) struct SearchService<'a> { + entities: HashMap>, } -pub(crate) fn prepare<'a>(sub_matches: &'a ArgMatches) -> Result, Error> { - let gitlab_token = match ArgGitlabToken::parse(sub_matches) { - Ok(arg) => arg.value(), - Err(err) => return Err(err), - }; - let gitlab_url = match ArgGitlabUrl::parse(sub_matches) { - Ok(arg) => arg.value(), - Err(err) => return Err(err), - }; +impl<'a> SearchService<'a> { + pub fn new(gitlab_client: &'a Gitlab) -> Self { + let mut entities: HashMap> = HashMap::new(); + entities.insert(EntityName::PROJECTS, Box::new(Projects::new(gitlab_client))); + entities.insert(EntityName::GROUPS, Box::new(Groups::new(gitlab_client))); + entities.insert(EntityName::USERS, Box::new(Users::new(gitlab_client))); + SearchService { entities } + } - // Connect to gitlab - let gitlab_client: Gitlab = match Gitlab::new( - gitlab_url.to_string(), - gitlab_token.to_string(), - ) { - Ok(g) => g, - Err(_err) => return Err(Error::new(ErrorKind::Other, _err)), - }; + pub fn search(&self, entity_name: &str, query: &str) -> Result<()> { + let entity_name = EntityName::from_str(entity_name)?; + let entity = self.entities.get(&entity_name).ok_or_else(|| { + Error::new( + std::io::ErrorKind::NotFound, + format!("Could not resolve entity with name {entity_name}"), + ) + })?; - // Get search subcommand - let search_sub = sub_matches.subcommand(); + entity.search(query) + } +} - Ok(SearchCmd { - search_sub, - gitlab_client, - }) +#[derive(Hash, PartialEq, Eq, Debug)] +pub(crate) enum EntityName { + GROUPS, + PROJECTS, + USERS, } -impl<'a> Cmd<'a> for SearchCmd<'a> { - fn exec(&self) -> Result<(), Error> { - let result; - match self.search_sub { - Some(("users", sub_matches)) => { - result = match users_cmd::prepare(sub_matches, &self.gitlab_client) { - Ok(cmd) => cmd.exec(), - Err(err) => Err(err), - }; - } - Some(("projects", sub_matches)) => { - result = match projects_cmd::prepare(sub_matches, &self.gitlab_client) { - Ok(cmd) => cmd.exec(), - Err(err) => Err(err), - }; - } - Some(("groups", sub_matches)) => { - result = match groups_cmd::prepare(sub_matches, &self.gitlab_client) { - Ok(cmd) => cmd.exec(), - Err(err) => Err(err), - }; - } - _ => { - return Err(Error::new( - std::io::ErrorKind::InvalidInput, - "You should specify what you are looking for, please use help", - )); - } +impl FromStr for EntityName { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s { + "groups" => Ok(Self::GROUPS), + "projects" => Ok(Self::PROJECTS), + "users" => Ok(Self::USERS), + name => Err(Error::new( + std::io::ErrorKind::InvalidInput, + format!("Entity with name {name} does not exist"), + )), } - return result; + } +} + +impl Display for EntityName { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{:?}", self) } } diff --git a/src/cmd/search/commands.rs b/src/cmd/search/commands.rs new file mode 100644 index 0000000..187797b --- /dev/null +++ b/src/cmd/search/commands.rs @@ -0,0 +1,35 @@ +use crate::args::{gitlab_token::ArgGitlabToken, gitlab_url::ArgGitlabUrl, Args}; +use clap::{arg, Command}; + +pub(crate) fn add_search_cmd() -> Command<'static> { + return Command::new("search") + .aliases(&["s", "find"]) + .about("Search for GitLab entities") + .arg(ArgGitlabToken::add()) + .arg(ArgGitlabUrl::add()) + .arg_required_else_help(true) + .subcommand(add_search_projects_cmd()) + .subcommand(add_search_users_cmd()) + .subcommand(add_search_groups_cmd()); +} + +fn add_search_projects_cmd() -> Command<'static> { + return Command::new("projects") + .about("Look for GitLab projects") + .aliases(&["p", "project"]) + .arg(arg!( "What you are looking for, mate?")); +} + +fn add_search_users_cmd() -> Command<'static> { + return Command::new("users") + .about("Look for GitLab users") + .aliases(&["u", "user"]) + .arg(arg!( "What you are looking for, mate?")); +} + +fn add_search_groups_cmd() -> Command<'static> { + return Command::new("groups") + .about("Look for GitLab groups") + .aliases(&["g", "group"]) + .arg(arg!( "What you are looking for, mate?")); +} diff --git a/src/cmd/search/groups.rs b/src/cmd/search/groups.rs new file mode 100644 index 0000000..6cf7c95 --- /dev/null +++ b/src/cmd/search/groups.rs @@ -0,0 +1,42 @@ +use std::io::{Error, ErrorKind, Result}; + +use gitlab::{ + api::{groups, Query}, + Gitlab, +}; +use tabled::Table; + +use crate::{ + gitlab::Group, + output::{out_message::OutMessage, out_spinner::OutSpinner}, +}; + +use super::SearchEntity; + +pub(crate) struct Groups<'a> { + gitlab_client: &'a Gitlab, +} + +impl<'a> Groups<'a> { + pub fn new(gitlab_client: &'a Gitlab) -> Self { + Groups { gitlab_client } + } +} + +impl<'a> SearchEntity for Groups<'a> { + fn search(&self, query: &str) -> Result<()> { + let spinner = OutSpinner::spinner_start("Looking for groups".to_string()); + let groups = match groups::Groups::builder().search(query).build() { + Ok(q) => q, + Err(err) => { + spinner.spinner_failure(err.to_string()); + return Err(Error::new(ErrorKind::ConnectionRefused, err)); + } + }; + let output: Vec = groups.query(self.gitlab_client).unwrap(); + spinner.spinner_success("That's what we've got for ya".to_string()); + let table = Table::new(&output); + OutMessage::message_empty(format!("{}", table).as_str()); + Ok(()) + } +} diff --git a/src/cmd/search/groups_cmd.rs b/src/cmd/search/groups_cmd.rs deleted file mode 100644 index da5e8a5..0000000 --- a/src/cmd/search/groups_cmd.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::io::{Error, ErrorKind}; - -use clap::{arg, ArgMatches, Command}; -use gitlab::{ - api::{groups, Query}, - Gitlab, -}; -use tabled::Table; - -use crate::{ - cmd::Cmd, - gitlab::Group, - output::{out_message::OutMessage, out_spinner::OutSpinner}, -}; - -pub(crate) fn find_groups<'a>() -> Command<'a> { - return Command::new("groups") - .about("Look for GitLab groups") - .aliases(&["g", "group"]) - .arg(arg!( "What you are looking for, mate?")); -} - -pub(crate) fn prepare<'a>( - sub_matches: &'a ArgMatches, - gitlab_client: &'a Gitlab, -) -> Result, Error> { - let search_string = sub_matches.value_of("SEARCH").ok_or(Error::new( - std::io::ErrorKind::InvalidInput, - "Whatcha lookin' for, mate?", - )); - if search_string.is_err() { - return Err(search_string.err().unwrap()); - } - - Ok(GroupsCmd { - search_string: search_string.unwrap().to_string(), - gitlab_client, - }) -} -struct GroupsCmd<'a> { - search_string: String, - gitlab_client: &'a Gitlab, -} - -impl<'a> Cmd<'a> for GroupsCmd<'a> { - fn exec(&self) -> Result<(), Error> { - let spinner = OutSpinner::spinner_start("Looking for groups".to_string()); - let groups = match groups::Groups::builder() - .search(&self.search_string) - .build() - { - Ok(q) => q, - Err(err) => { - spinner.spinner_failure(err.to_string()); - return Err(Error::new(ErrorKind::ConnectionRefused, err)); - } - }; - let output: Vec = groups.query(self.gitlab_client).unwrap(); - spinner.spinner_success("That's what we've got for ya".to_string()); - let table = Table::new(&output); - OutMessage::message_empty(format!("{}", table).as_str()); - Ok(()) - } -} \ No newline at end of file diff --git a/src/cmd/search/projects.rs b/src/cmd/search/projects.rs new file mode 100644 index 0000000..401bc16 --- /dev/null +++ b/src/cmd/search/projects.rs @@ -0,0 +1,42 @@ +use std::io::{Error, ErrorKind, Result}; + +use gitlab::{ + api::{projects, Query}, + Gitlab, +}; +use tabled::Table; + +use crate::{ + gitlab::Project, + output::{out_message::OutMessage, out_spinner::OutSpinner}, +}; + +use super::SearchEntity; + +pub(crate) struct Projects<'a> { + gitlab_client: &'a Gitlab, +} + +impl<'a> Projects<'a> { + pub fn new(gitlab_client: &'a Gitlab) -> Self { + Projects { gitlab_client } + } +} + +impl<'a> SearchEntity for Projects<'a> { + fn search(&self, query: &str) -> Result<()> { + let spinner = OutSpinner::spinner_start("Looking for projects".to_string()); + let projects = match projects::Projects::builder().search(query).build() { + Ok(q) => q, + Err(err) => { + spinner.spinner_failure(err.to_string()); + return Err(Error::new(ErrorKind::ConnectionRefused, err)); + } + }; + let output: Vec = projects.query(self.gitlab_client).unwrap(); + spinner.spinner_success("That's what we've got for ya".to_string()); + let table = Table::new(&output); + OutMessage::message_empty(format!("{}", table).as_str()); + Ok(()) + } +} diff --git a/src/cmd/search/projects_cmd.rs b/src/cmd/search/projects_cmd.rs deleted file mode 100644 index 5f7b540..0000000 --- a/src/cmd/search/projects_cmd.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::io::{Error, ErrorKind}; - -use clap::{arg, ArgMatches, Command}; -use gitlab::{ - api::{projects, Query}, - Gitlab, -}; -use tabled::Table; - -use crate::{ - cmd::Cmd, - gitlab::Project, - output::{out_message::OutMessage, out_spinner::OutSpinner}, -}; - -pub(crate) fn find_projects<'a>() -> Command<'a> { - return Command::new("projects") - .about("Look for GitLab projects") - .aliases(&["p", "project"]) - .arg(arg!( "What you are looking for, mate?")); -} - -pub(crate) fn prepare<'a>( - sub_matches: &'a ArgMatches, - gitlab_client: &'a Gitlab, -) -> Result, Error> { - let search_string = sub_matches.value_of("SEARCH").ok_or(Error::new( - std::io::ErrorKind::PermissionDenied, - "whatcha lookin' for, mate?", - )); - if search_string.is_err() { - return Err(search_string.err().unwrap()); - } - - Ok(ProjectsCmd { - search_string: search_string.unwrap().to_string(), - gitlab_client, - }) -} -struct ProjectsCmd<'a> { - search_string: String, - gitlab_client: &'a Gitlab, -} - -impl<'a> Cmd<'a> for ProjectsCmd<'a> { - fn exec(&self) -> Result<(), Error> { - let spinner = OutSpinner::spinner_start("Looking for projects".to_string()); - let projects = match projects::Projects::builder() - .search(&self.search_string) - .build() - { - Ok(q) => q, - Err(err) => { - spinner.spinner_failure(err.to_string()); - return Err(Error::new(ErrorKind::ConnectionRefused, err)); - } - }; - let output: Vec = projects.query(self.gitlab_client).unwrap(); - spinner.spinner_success("That's what we've got for ya".to_string()); - let table = Table::new(&output); - OutMessage::message_empty(format!("{}", table).as_str()); - Ok(()) - } -} diff --git a/src/cmd/search/users.rs b/src/cmd/search/users.rs new file mode 100644 index 0000000..35b760d --- /dev/null +++ b/src/cmd/search/users.rs @@ -0,0 +1,42 @@ +use std::io::{Error, ErrorKind, Result}; + +use gitlab::{ + api::{users, Query}, + Gitlab, +}; +use tabled::Table; + +use crate::{ + gitlab::User, + output::{out_message::OutMessage, out_spinner::OutSpinner}, +}; + +use super::SearchEntity; + +pub(crate) struct Users<'a> { + gitlab_client: &'a Gitlab, +} + +impl<'a> Users<'a> { + pub fn new(gitlab_client: &'a Gitlab) -> Self { + Users { gitlab_client } + } +} + +impl<'a> SearchEntity for Users<'a> { + fn search(&self, query: &str) -> Result<()> { + let spinner = OutSpinner::spinner_start("Looking for users".to_string()); + let users = match users::Users::builder().search(query).build() { + Ok(q) => q, + Err(err) => { + spinner.spinner_failure(err.to_string()); + return Err(Error::new(ErrorKind::ConnectionRefused, err)); + } + }; + let output: Vec = users.query(self.gitlab_client).unwrap(); + spinner.spinner_success("That's what we've got for ya".to_string()); + let table = Table::new(&output); + OutMessage::message_empty(format!("{}", table).as_str()); + Ok(()) + } +} diff --git a/src/cmd/search/users_cmd.rs b/src/cmd/search/users_cmd.rs deleted file mode 100644 index c15fe5f..0000000 --- a/src/cmd/search/users_cmd.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::io::{Error, ErrorKind}; - -use clap::{arg, ArgMatches, Command}; -use gitlab::{ - api::{users, Query}, - Gitlab, -}; -use tabled::Table; - -use crate::{cmd::Cmd, gitlab::User, output::{out_message::OutMessage, out_spinner::OutSpinner}}; - -pub(crate) fn find_users<'a>() -> Command<'a> { - return Command::new("users") - .about("Look for GitLab users") - .aliases(&["u", "user"]) - .arg(arg!( "What you are looking for, mate?")); -} - -pub(crate) fn prepare<'a>( - sub_matches: &'a ArgMatches, - gitlab_client: &'a Gitlab, -) -> Result, Error> { - let search_string = sub_matches.value_of("SEARCH").ok_or(Error::new( - std::io::ErrorKind::PermissionDenied, - "whatcha lookin' for, mate?", - )); - if search_string.is_err() { - return Err(search_string.err().unwrap()); - } - - Ok(UsersCmd { - search_string: search_string.unwrap().to_string(), - gitlab_client, - }) -} -struct UsersCmd<'a> { - search_string: String, - gitlab_client: &'a Gitlab, -} - -impl<'a> Cmd<'a> for UsersCmd<'a> { - fn exec(&self) -> Result<(), Error> { - let spinner = OutSpinner::spinner_start("Looking for users".to_string()); - let users = match users::Users::builder().search(&self.search_string).build() { - Ok(q) => q, - Err(err) => { - spinner.spinner_failure(err.to_string()); - return Err(Error::new(ErrorKind::ConnectionRefused, err)); - } - }; - let output: Vec = users.query(self.gitlab_client).unwrap(); - spinner.spinner_success("That's what we've got for ya".to_string()); - let table = Table::new(&output); - OutMessage::message_empty(format!("{}", table).as_str()); - Ok(()) - } -} diff --git a/src/main.rs b/src/main.rs index c3764a5..3c1bb5a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,10 +4,12 @@ mod gitlab; mod output; mod types; -use clap::Command; +use ::gitlab::Gitlab; +use args::{gitlab_token::ArgGitlabToken, gitlab_url::ArgGitlabUrl, Args}; +use clap::{ArgMatches, Command}; use cmd::{ init::{self, add_init_cmd}, - search::{self, add_search_cmd}, + search::{commands::add_search_cmd, SearchService}, sync::{self, add_sync_cmd}, teams::{self, add_teams_cmd}, upgrade::{self, add_upgrade_cmd}, @@ -35,50 +37,7 @@ fn main() { .subcommand(add_upgrade_cmd()) .get_matches(); - let result: Result<(), Error>; - - match matches.subcommand() { - Some(("init", sub_matches)) => { - result = match init::prepare(sub_matches) { - Ok(cmd) => cmd.exec(), - Err(err) => Err(err), - }; - } - Some(("sync", sub_matches)) => { - result = match sync::prepare(sub_matches) { - Ok(cmd) => cmd.exec(), - Err(err) => Err(err), - }; - } - Some(("users", sub_matches)) => { - result = match users::prepare(sub_matches) { - Ok(cmd) => cmd.exec(), - Err(err) => Err(err), - }; - } - Some(("teams", sub_matches)) => { - result = match teams::prepare(sub_matches) { - Ok(cmd) => cmd.exec(), - Err(err) => Err(err), - }; - } - Some(("search", sub_matches)) => { - result = match search::prepare(sub_matches) { - Ok(cmd) => cmd.exec(), - Err(err) => Err(err), - }; - } - Some(("upgrade", sub_matches)) => { - result = match upgrade::prepare(sub_matches) { - Ok(cmd) => cmd.exec(), - Err(err) => Err(err), - }; - } - - _ => result = Err(Error::new(ErrorKind::InvalidInput, "No command provided")), - } - - match result { + match get_result(matches) { Err(err) => { OutSum::sum_failure(&err.to_string()); exit(1); @@ -88,3 +47,34 @@ fn main() { } } } + +fn get_result(matches: ArgMatches) -> Result<(), Error> { + match matches.subcommand() { + Some(("init", sub_matches)) => init::prepare(sub_matches).map(|cmd| cmd.exec())?, + Some(("sync", sub_matches)) => sync::prepare(sub_matches).map(|cmd| cmd.exec())?, + Some(("users", sub_matches)) => users::prepare(sub_matches).map(|cmd| cmd.exec())?, + Some(("teams", sub_matches)) => teams::prepare(sub_matches).map(|cmd| cmd.exec())?, + Some(("search", sub_matches)) => { + if let Some((entity_name, sub_matches)) = sub_matches.subcommand() { + let gitlab_token = ArgGitlabToken::parse(sub_matches).map(|arg| arg.value())?; + let gitlab_url = ArgGitlabUrl::parse(sub_matches).map(|arg| arg.value())?; + let gitlab_client = + Gitlab::new(gitlab_url.to_string(), gitlab_token.to_string()).unwrap(); + + let query = sub_matches.value_of("SEARCH").ok_or(Error::new( + std::io::ErrorKind::PermissionDenied, + "whatcha lookin' for, mate?", + ))?; + + return SearchService::new(&gitlab_client).search(entity_name, query); + } + + return Err(Error::new( + std::io::ErrorKind::InvalidInput, + "You should specify what you are looking for, please use help", + )); + } + Some(("upgrade", sub_matches)) => upgrade::prepare(sub_matches).map(|cmd| cmd.exec())?, + _ => Err(Error::new(ErrorKind::InvalidInput, "No command provided")), + } +}