refactor(cli): clean up code
cleared out main moved subcommand handling to a method on each arg struct moved from many, many expects to returning errors
This commit is contained in:
+100
-1
@@ -1,5 +1,11 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
|
||||||
|
use crate::config;
|
||||||
|
use crate::config::Profile;
|
||||||
|
use crate::git;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(version, about = "a cli to change git users")]
|
#[command(version, about = "a cli to change git users")]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
@@ -9,13 +15,47 @@ pub struct Cli {
|
|||||||
|
|
||||||
#[derive(Debug, Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub enum Commands {
|
pub enum Commands {
|
||||||
List,
|
List(ListArgs),
|
||||||
Use(UseArgs),
|
Use(UseArgs),
|
||||||
Add(AddArgs),
|
Add(AddArgs),
|
||||||
Remove(RemoveArgs),
|
Remove(RemoveArgs),
|
||||||
Current(CurrentArgs),
|
Current(CurrentArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Commands {
|
||||||
|
pub fn execute(self) -> Result<(), Box<dyn Error>> {
|
||||||
|
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<dyn Error>> {
|
||||||
|
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)]
|
#[derive(Debug, Args)]
|
||||||
pub struct UseArgs {
|
pub struct UseArgs {
|
||||||
pub profile_name: String,
|
pub profile_name: String,
|
||||||
@@ -23,6 +63,23 @@ pub struct UseArgs {
|
|||||||
pub global: bool,
|
pub global: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl UseArgs {
|
||||||
|
fn execute(self) -> Result<(), Box<dyn Error>> {
|
||||||
|
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)]
|
#[derive(Debug, Args)]
|
||||||
pub struct AddArgs {
|
pub struct AddArgs {
|
||||||
pub profile_name: String,
|
pub profile_name: String,
|
||||||
@@ -32,13 +89,55 @@ pub struct AddArgs {
|
|||||||
pub email: String,
|
pub email: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AddArgs {
|
||||||
|
fn execute(self) -> Result<(), Box<dyn Error>> {
|
||||||
|
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)]
|
#[derive(Debug, Args)]
|
||||||
pub struct RemoveArgs {
|
pub struct RemoveArgs {
|
||||||
pub profile_name: String,
|
pub profile_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RemoveArgs {
|
||||||
|
fn execute(self) -> Result<(), Box<dyn Error>> {
|
||||||
|
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)]
|
#[derive(Debug, Args)]
|
||||||
pub struct CurrentArgs {
|
pub struct CurrentArgs {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub global: bool,
|
pub global: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CurrentArgs {
|
||||||
|
fn execute(self) -> Result<(), Box<dyn Error>> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+20
-22
@@ -1,6 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
error::Error,
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::{BufReader, BufWriter, Error},
|
io::{BufReader, BufWriter},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -13,39 +14,36 @@ pub struct Profile {
|
|||||||
pub email: String,
|
pub email: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_config_dir() -> PathBuf {
|
fn get_config_dir() -> Result<PathBuf, Box<dyn Error>> {
|
||||||
let mut config_dir = dirs::config_dir().expect("unable to find config dir");
|
let mut config_dir = dirs::config_dir().ok_or("unable to get config dir")?;
|
||||||
config_dir.push("git-su");
|
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<dyn Error + 'static>> {
|
||||||
if !file.exists() {
|
if !file.exists() {
|
||||||
fs::write(file, content)
|
fs::write(file, content)?;
|
||||||
} else {
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_profile_list() -> Result<Vec<Profile>, String> {
|
pub fn get_profile_list() -> Result<Vec<Profile>, Box<dyn Error>> {
|
||||||
let config_dir = get_config_dir();
|
let config_dir = get_config_dir()?;
|
||||||
fs::create_dir_all(&config_dir).expect("unable to create config directory");
|
fs::create_dir_all(&config_dir)?;
|
||||||
let profile_file = config_dir.join("profiles.json");
|
let profile_file = config_dir.join("profiles.json");
|
||||||
create_if_missing(profile_file.as_path(), "[]").expect("unable to create profile file");
|
create_if_missing(profile_file.as_path(), "[]")?;
|
||||||
let profile_file = File::open(&profile_file).expect("unable to open profile file");
|
let profile_file = File::open(&profile_file)?;
|
||||||
let reader = BufReader::new(profile_file);
|
let reader = BufReader::new(profile_file);
|
||||||
Ok(
|
let profiles = serde_json::from_reader::<BufReader<File>, Vec<Profile>>(reader)?;
|
||||||
serde_json::from_reader::<BufReader<File>, Vec<Profile>>(reader)
|
Ok(profiles)
|
||||||
.expect("unable to parse json"),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_profile_list(profiles: Vec<Profile>) -> Result<(), String> {
|
pub fn write_profile_list(profiles: Vec<Profile>) -> Result<(), Box<dyn Error>> {
|
||||||
let config_dir = get_config_dir();
|
let config_dir = get_config_dir()?;
|
||||||
fs::create_dir_all(&config_dir).expect("unable to create config directory");
|
fs::create_dir_all(&config_dir)?;
|
||||||
let profile_file = config_dir.join("profiles.json");
|
let profile_file = config_dir.join("profiles.json");
|
||||||
let profile_file = File::create(&profile_file).expect("unable to create profile file");
|
let profile_file = File::create(&profile_file)?;
|
||||||
let writer = BufWriter::new(profile_file);
|
let writer = BufWriter::new(profile_file);
|
||||||
serde_json::to_writer_pretty(writer, &profiles).expect("unable to write json");
|
serde_json::to_writer_pretty(writer, &profiles)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
use std::{error::Error, process::Command};
|
||||||
|
|
||||||
|
pub fn get_git_config(key: &str, global: bool) -> Result<String, Box<dyn Error>> {
|
||||||
|
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<dyn Error>> {
|
||||||
|
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<String, Box<dyn Error>> {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-89
@@ -1,99 +1,14 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use crate::cli::Commands;
|
|
||||||
use crate::config::Profile;
|
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
mod config;
|
mod config;
|
||||||
mod utils;
|
mod git;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = cli::Cli::parse();
|
let args = cli::Cli::parse();
|
||||||
|
|
||||||
match args.command {
|
if let Err(why) = args.command.execute() {
|
||||||
Commands::List => {
|
eprintln!("error: {}", why);
|
||||||
let profiles = config::get_profile_list().expect("unable to get list of profiles");
|
std::process::exit(1);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
use std::process::Command;
|
|
||||||
|
|
||||||
pub fn run_command(command: &str, args: &[&str]) -> Result<String, String> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user