mod parser; pub mod user_info; pub mod vcs; use crate::vcs::git; use comfy_table::Table; use humansize::{format_size, BINARY}; use parser::*; use rustyline::{error::ReadlineError, DefaultEditor}; use std::{io, ops::ControlFlow}; use thiserror::Error; pub fn run_command(user_input: &str) -> Result, ShackleError> { match user_input.parse::() { Err(parse_error) => { println!("{}", parse_error); } Ok(ShackleCommand::Exit) => { return Ok(ControlFlow::Break(())); } Ok(ShackleCommand::List(ListArgs { verbose })) => { let mut table = Table::new(); if !verbose { table.set_header(vec!["path", "description"]); let listing = git::list()?; for meta in listing { table.add_row(vec![meta.path.display().to_string(), meta.description]); } } else { table.set_header(vec!["path", "description", "size"]); let listing = git::list_verbose()?; for meta in listing { table.add_row(vec![ meta.path.display().to_string(), meta.description, format_size(meta.size, BINARY), ]); } } println!("{table}"); } Ok(ShackleCommand::SetDescription(SetDescriptionArgs { directory, description, })) => { git::set_description(&directory, &description)?; println!("Successfully updated description"); } Ok(ShackleCommand::SetBranch(SetBranchArgs { directory, branch })) => { git::set_branch(&directory, &branch)?; println!("Successfully updated branch"); } Ok(ShackleCommand::Init(InitArgs { repo_name, group, description, branch, })) => { let init_result = git::init(&repo_name, &group, &description, &branch)?; println!("Successfully created \"{}\"", init_result.path.display()); } Ok(ShackleCommand::Delete(DeleteArgs { directory })) => { if confirm_risky_action(format!( "Are you sure you want to delete \"{}\"?", directory.display() ))? { git::delete(&directory)?; println!("Successfully deleted \"{}\"", directory.display()); } else { println!("Action cancelled"); } } Ok(ShackleCommand::Housekeeping(HousekeepingArgs { directory })) => match directory { Some(directory) => { git::housekeeping(&directory)?; println!( "Successfully did housekeeping on \"{}\"", directory.display() ); } None => { let list = git::list()?; for repo in list { git::housekeeping(&repo.path)?; println!( "Successfully did housekeeping on \"{}\"", repo.path.display() ); } } }, Ok(ShackleCommand::GitUploadPack(upload_pack_args)) => { git::upload_pack(&upload_pack_args)?; } Ok(ShackleCommand::GitReceivePack(receive_pack_args)) => { git::receive_pack(&receive_pack_args)?; } } Ok(ControlFlow::Continue(())) } fn confirm_risky_action(prompt: String) -> Result { let mut rl = DefaultEditor::new()?; loop { let user_input = rl .readline(&format!("{} (yes/no) ", prompt)) .map(|user_input| user_input.to_lowercase())?; match user_input.trim() { "" => {} "yes" => { return Ok(true); } "no" => { return Ok(false); } _ => { println!("Please answer 'yes' or 'no'."); } } } } #[derive(Error, Debug)] pub enum ShackleError { #[error(transparent)] IoError(#[from] io::Error), #[error(transparent)] NixError(#[from] nix::errno::Errno), #[error(transparent)] GitError(#[from] git2::Error), #[error(transparent)] ReadlineError(#[from] ReadlineError), #[error("Could not get the current user name")] UserReadError, #[error("Path is not accessible")] InvalidDirectory, #[error("Unknown group")] InvalidGroup, }