mod git; mod parser; use clap::Parser; use parser::*; use std::{io, io::Write, ops::ControlFlow, process::Command}; use thiserror::Error; /// Shackle Shell - A replacement for git-shell with repo management commands built in. #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { /// Run a single shell command and exit #[arg(short, long)] command: Option, } fn prompt() -> Result<(), ShackleError> { print!("> "); io::stdout().flush()?; Ok(()) } fn read_stdin() -> Result { let mut buffer = String::new(); io::stdin().read_line(&mut buffer)?; Ok(buffer) } fn main() -> Result<(), ShackleError> { let args = Args::parse(); match args.command { Some(user_input) => { run_command(user_input)?; } None => { run_interactive_loop()?; } } Ok(()) } fn run_command(user_input: String) -> Result, ShackleError> { match user_input.parse::() { Err(parse_error) => { println!("{}", parse_error); } Ok(ShackleCommand::Whitespace) => {} Ok(ShackleCommand::Exit) => { return Ok(ControlFlow::Break(())); } Ok(ShackleCommand::GitInit(GitInitArgs { repo_name })) => { git::init(&repo_name)?; // TODO should report this error differently println!("Successfully created {}.git", repo_name); } Ok(ShackleCommand::GitUploadPack(upload_pack_args)) => { let mut command = Command::new("git-upload-pack"); if upload_pack_args.strict { command.arg("strict"); } if upload_pack_args.no_strict { command.arg("no-strict"); } if let Some(timeout) = upload_pack_args.timeout { command.args(["timeout", &timeout.to_string()]); } if upload_pack_args.stateless_rpc { command.arg("stateless-rpc"); } if upload_pack_args.advertise_refs { command.arg("advertise-refs"); } // TODO: This should definitely be part of the arg parsing! command.arg(&upload_pack_args.directory.trim_matches('\'')); command.spawn()?.wait()?; } } Ok(ControlFlow::Continue(())) } fn run_interactive_loop() -> Result<(), ShackleError> { loop { prompt()?; let user_input = read_stdin()?; // TODO: should this report errors differently? Most of the errors are from user actions. let control_flow = run_command(user_input)?; if control_flow.is_break() { break; } } Ok(()) } pub enum FlowControl {} #[derive(Error, Debug)] pub enum ShackleError { #[error(transparent)] IoError(#[from] io::Error), #[error(transparent)] GitError(#[from] git2::Error), }