diff --git a/src/cli.rs b/src/cli.rs index b451f7a..5d7b628 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,5 +1,11 @@ +use std::error::Error; + use clap::{Args, Parser, Subcommand}; +use crate::config; +use crate::config::Profile; +use crate::git; + #[derive(Debug, Parser)] #[command(version, about = "a cli to change git users")] pub struct Cli { @@ -9,13 +15,47 @@ pub struct Cli { #[derive(Debug, Subcommand)] pub enum Commands { - List, + List(ListArgs), Use(UseArgs), Add(AddArgs), Remove(RemoveArgs), Current(CurrentArgs), } +impl Commands { + pub fn execute(self) -> Result<(), Box> { + match self { + Commands::List(args) => args.execute(), + Commands::Use(args) => args.execute(), + Commands::Add(args) => args.execute(), + Commands::Remove(args) => args.execute(), + Commands::Current(args) => args.execute(), + } + } +} + +#[derive(Debug, Args)] +pub struct ListArgs; + +impl ListArgs { + fn execute(self) -> Result<(), Box> { + let profiles = config::get_profile_list()?; + if profiles.is_empty() { + println!("no profiles found"); + return Ok(()); + } + + println!("{:<15} {:<20} {:<25}", "profile", "name", "email"); + for profile in profiles { + println!( + "{:<15} {:<20} {:<25}", + profile.name, profile.username, profile.email + ); + } + Ok(()) + } +} + #[derive(Debug, Args)] pub struct UseArgs { pub profile_name: String, @@ -23,6 +63,23 @@ pub struct UseArgs { pub global: bool, } +impl UseArgs { + fn execute(self) -> Result<(), Box> { + let profiles = config::get_profile_list()?; + let profile = profiles.iter().find(|p| p.name == self.profile_name); + if profile.is_none() { + println!("profile not found"); + return Ok(()); + } + + let profile = profile.unwrap(); + + git::set_git_config("user.name", &profile.username, self.global)?; + git::set_git_config("user.email", &profile.email, self.global)?; + Ok(()) + } +} + #[derive(Debug, Args)] pub struct AddArgs { pub profile_name: String, @@ -32,13 +89,55 @@ pub struct AddArgs { pub email: String, } +impl AddArgs { + fn execute(self) -> Result<(), Box> { + let mut profiles = config::get_profile_list()?; + let existing_profile = profiles.iter().position(|p| p.name == self.profile_name); + if existing_profile.is_some() { + println!("profile already exists"); + return Ok(()); + } + profiles.push(Profile { + name: self.profile_name, + username: self.name, + email: self.email, + }); + config::write_profile_list(profiles)?; + println!("profile added"); + Ok(()) + } +} + #[derive(Debug, Args)] pub struct RemoveArgs { pub profile_name: String, } +impl RemoveArgs { + fn execute(self) -> Result<(), Box> { + let mut profiles = config::get_profile_list()?; + let index = profiles + .iter() + .position(|p| p.name == self.profile_name) + .expect("unable to find profile"); + profiles.remove(index); + config::write_profile_list(profiles)?; + println!("profile removed"); + Ok(()) + } +} + #[derive(Debug, Args)] pub struct CurrentArgs { #[arg(short, long)] pub global: bool, } + +impl CurrentArgs { + fn execute(self) -> Result<(), Box> { + let name = git::get_git_config("user.name", self.global)?; + let email = git::get_git_config("user.email", self.global)?; + println!("current user: {} ({})", name, email); + Ok(()) + } +} diff --git a/src/config.rs b/src/config.rs index 98af736..7229606 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,7 @@ use std::{ + error::Error, fs::{self, File}, - io::{BufReader, BufWriter, Error}, + io::{BufReader, BufWriter}, path::{Path, PathBuf}, }; @@ -13,39 +14,36 @@ pub struct Profile { pub email: String, } -fn get_config_dir() -> PathBuf { - let mut config_dir = dirs::config_dir().expect("unable to find config dir"); +fn get_config_dir() -> Result> { + let mut config_dir = dirs::config_dir().ok_or("unable to get config dir")?; config_dir.push("git-su"); - config_dir + Ok(config_dir) } -fn create_if_missing(file: &Path, content: &str) -> Result<(), Error> { +fn create_if_missing(file: &Path, content: &str) -> Result<(), Box> { if !file.exists() { - fs::write(file, content) - } else { - Ok(()) + fs::write(file, content)?; } -} - -pub fn get_profile_list() -> Result, String> { - let config_dir = get_config_dir(); - fs::create_dir_all(&config_dir).expect("unable to create config directory"); - let profile_file = config_dir.join("profiles.json"); - create_if_missing(profile_file.as_path(), "[]").expect("unable to create profile file"); - let profile_file = File::open(&profile_file).expect("unable to open profile file"); - let reader = BufReader::new(profile_file); - Ok( - serde_json::from_reader::, Vec>(reader) - .expect("unable to parse json"), - ) -} - -pub fn write_profile_list(profiles: Vec) -> Result<(), String> { - let config_dir = get_config_dir(); - fs::create_dir_all(&config_dir).expect("unable to create config directory"); - let profile_file = config_dir.join("profiles.json"); - let profile_file = File::create(&profile_file).expect("unable to create profile file"); - let writer = BufWriter::new(profile_file); - serde_json::to_writer_pretty(writer, &profiles).expect("unable to write json"); + Ok(()) +} + +pub fn get_profile_list() -> Result, Box> { + let config_dir = get_config_dir()?; + fs::create_dir_all(&config_dir)?; + let profile_file = config_dir.join("profiles.json"); + create_if_missing(profile_file.as_path(), "[]")?; + let profile_file = File::open(&profile_file)?; + let reader = BufReader::new(profile_file); + let profiles = serde_json::from_reader::, Vec>(reader)?; + Ok(profiles) +} + +pub fn write_profile_list(profiles: Vec) -> Result<(), Box> { + let config_dir = get_config_dir()?; + fs::create_dir_all(&config_dir)?; + let profile_file = config_dir.join("profiles.json"); + let profile_file = File::create(&profile_file)?; + let writer = BufWriter::new(profile_file); + serde_json::to_writer_pretty(writer, &profiles)?; Ok(()) } diff --git a/src/git.rs b/src/git.rs new file mode 100644 index 0000000..c79d68f --- /dev/null +++ b/src/git.rs @@ -0,0 +1,35 @@ +use std::{error::Error, process::Command}; + +pub fn get_git_config(key: &str, global: bool) -> Result> { + let args = if global { + vec!["config", "--global", key] + } else { + vec!["config", key] + }; + + let result = run_command("git", &args)?; + Ok(result.trim().to_string()) +} + +pub fn set_git_config(key: &str, value: &str, global: bool) -> Result<(), Box> { + let args = if global { + vec!["config", "--global", key, value] + } else { + vec!["config", key, value] + }; + + let _ = run_command("git", &args)?; + Ok(()) +} + +fn run_command(command: &str, args: &[&str]) -> Result> { + let child = Command::new(command).args(args).output()?; + + if child.status.success() { + let stdout = String::from_utf8(child.stdout)?; + Ok(stdout.to_owned()) + } else { + let stderr = String::from_utf8(child.stderr)?; + Ok(stderr.to_owned()) + } +} diff --git a/src/main.rs b/src/main.rs index 9c2314e..f418d2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,99 +1,14 @@ use clap::Parser; -use crate::cli::Commands; -use crate::config::Profile; - mod cli; mod config; -mod utils; +mod git; fn main() { let args = cli::Cli::parse(); - match args.command { - Commands::List => { - let profiles = config::get_profile_list().expect("unable to get list of profiles"); - if profiles.is_empty() { - println!("no profiles found"); - return; - } - - println!("{:<15} {:<20} {:<25}", "profile", "name", "email"); - for profile in profiles { - println!( - "{:<15} {:<20} {:<25}", - profile.name, profile.username, profile.email - ); - } - } - Commands::Use(args) => { - let profiles = config::get_profile_list().expect("unable to get list of profiles"); - let profile = profiles.iter().find(|p| p.name == args.profile_name); - if profile.is_none() { - println!("profile not found"); - return; - } - - let profile = profile.unwrap(); - - utils::run_command( - "git", - &add_global_if(&["config", "user.name", &profile.username], args.global), - ) - .expect("command failed"); - utils::run_command( - "git", - &add_global_if(&["config", "user.email", &profile.email], args.global), - ) - .expect("command failed"); - } - Commands::Add(args) => { - let mut profiles = config::get_profile_list().expect("unable to get list of profiles"); - let existing_profile = profiles.iter().position(|p| p.name == args.profile_name); - if existing_profile.is_some() { - println!("profile already exists"); - return; - } - profiles.push(Profile { - name: args.profile_name, - username: args.name, - email: args.email, - }); - config::write_profile_list(profiles).expect("unable to write profile file"); - println!("profile added") - } - Commands::Remove(args) => { - let mut profiles = config::get_profile_list().expect("unable to get list of profiles"); - let index = profiles - .iter() - .position(|p| p.name == args.profile_name) - .expect("unable to find profile"); - profiles.remove(index); - config::write_profile_list(profiles).expect("unable to write profile file"); - println!("profile removed") - } - Commands::Current(args) => { - let mut name = - utils::run_command("git", &add_global_if(&["config", "user.name"], args.global)) - .expect("command failed"); - let mut email = utils::run_command( - "git", - &add_global_if(&["config", "user.email"], args.global), - ) - .expect("command failed"); - - name.pop(); - email.pop(); - - println!("current user: {} ({})", name, email); - } + if let Err(why) = args.command.execute() { + eprintln!("error: {}", why); + std::process::exit(1); } } - -fn add_global_if<'a>(args: &[&'a str], global: bool) -> Vec<&'a str> { - let mut result = args.to_vec(); - if global { - result.insert(1, "--global"); - } - result -} diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 2742826..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::process::Command; - -pub fn run_command(command: &str, args: &[&str]) -> Result { - let child = Command::new(command) - .args(args) - .output() - .expect("failed to execute process"); - - if child.status.success() { - let stdout = String::from_utf8(child.stdout).unwrap(); - Ok(stdout.to_owned()) - } else { - let stderr = String::from_utf8(child.stderr).unwrap(); - Err(stderr) - } -}