diff options
-rw-r--r-- | Cargo.lock | 7 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | readme.org | 2 | ||||
-rw-r--r-- | src/main.rs | 4 | ||||
-rw-r--r-- | src/parser.rs | 21 | ||||
-rw-r--r-- | tests/cli.rs | 36 |
6 files changed, 64 insertions, 7 deletions
@@ -526,11 +526,18 @@ dependencies = [ "nom", "once_cell", "rexpect", + "shlex", "tempfile", "thiserror", ] [[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -10,6 +10,7 @@ clap = { version = "4.1.8", features = ["derive"] } git2 = { version = "0.16.1", default-features = false, features = ["vendored-libgit2"] } nom = "7.1.3" rexpect = "0.5.0" +shlex = "1.1.0" thiserror = "1.0.38" [dev-dependencies] @@ -21,7 +21,7 @@ Pijul. - [X] Isolation of workdir between tests - [X] git fetch with git upload-pack - [X] git push with git receive-pack -- [ ] proper shell argument lexing, with quote stuff https://lib.rs/crates/shlex +- [X] proper shell argument lexing, with quote stuff https://lib.rs/crates/shlex - [ ] restrict repos to only acceptable paths - [ ] git init of shared repos - [ ] history (only within same session) https://lib.rs/crates/rustyline diff --git a/src/main.rs b/src/main.rs index 50f5127..bcbfe1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,8 +51,8 @@ fn run_command(user_input: String) -> Result<ControlFlow<(), ()>, ShackleError> 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); + git::init(&repo_name)?; + println!("Successfully created \"{}.git\"", repo_name); } Ok(ShackleCommand::GitUploadPack(upload_pack_args)) => { let mut command = Command::new("git-upload-pack"); diff --git a/src/parser.rs b/src/parser.rs index 59dc7d8..89e0d76 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,5 +1,6 @@ use clap::{Parser, Subcommand}; use std::str::FromStr; +use thiserror::Error; #[derive(Parser, Clone, Debug, PartialEq, Eq)] #[command(name = "")] @@ -44,8 +45,16 @@ pub struct GitReceivePackArgs { pub directory: String, } +#[derive(Error, Debug)] +pub enum ParserError { + #[error(transparent)] + ClapError(#[from] clap::error::Error), + #[error("`{0}`")] + LexerError(String), +} + impl FromStr for ShackleCommand { - type Err = clap::error::Error; + type Err = ParserError; fn from_str(s: &str) -> Result<Self, Self::Err> { let trimmed = s.trim(); @@ -54,7 +63,15 @@ impl FromStr for ShackleCommand { } else if trimmed.len() == 0 { Ok(ShackleCommand::Whitespace) } else { - ShackleCommand::try_parse_from([""].into_iter().chain(trimmed.split_whitespace())) + let lexed = shlex::split(trimmed); + match lexed { + None => Err(ParserError::LexerError("Incomplete input".to_string())), + Some(lexed) => { + let parsed = + ShackleCommand::try_parse_from(["".to_owned()].into_iter().chain(lexed))?; + Ok(parsed) + } + } } } } diff --git a/tests/cli.rs b/tests/cli.rs index 6ace151..e6a59b3 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -90,7 +90,7 @@ fn reports_error_with_nonsense_input() -> Result<()> { fn can_init_a_new_git_repo() -> Result<()> { let mut c = spawn_interactive_process()?; c.p.send_line("git-init my-new-repo")?; - c.p.exp_string("Successfully created my-new-repo.git")?; + c.p.exp_string("Successfully created \"my-new-repo.git\"")?; expect_prompt(&mut c.p)?; Command::new("git") @@ -106,7 +106,39 @@ fn can_init_a_new_git_repo() -> Result<()> { #[test] fn runs_a_single_command_and_exit_with_cli_flag() -> Result<()> { let mut c = run_batch_command("git-init another-new-repo")?; - c.p.exp_string("Successfully created another-new-repo.git")?; + c.p.exp_string("Successfully created \"another-new-repo.git\"")?; c.p.exp_eof()?; Ok(()) } + +#[test] +fn allows_quotes_arguments() -> Result<()> { + let mut c = spawn_interactive_process()?; + c.p.send_line("\"git-init\" 'another-new-repo'")?; + c.p.exp_string("Successfully created \"another-new-repo.git\"")?; + Ok(()) +} + +#[test] +fn errors_with_an_open_double_quote() -> Result<()> { + let mut c = spawn_interactive_process()?; + c.p.send_line("\"git-init 'another-new-repo'")?; + c.p.exp_string("Incomplete input")?; + Ok(()) +} + +#[test] +fn errors_with_an_open_single_quote() -> Result<()> { + let mut c = spawn_interactive_process()?; + c.p.send_line("'git-init 'another-new-repo'")?; + c.p.exp_string("Incomplete input")?; + Ok(()) +} + +#[test] +fn allows_single_quotes_and_spaces_inside_double_quotes() -> Result<()> { + let mut c = spawn_interactive_process()?; + c.p.send_line("git-init \"shukkie's new repo\"")?; + c.p.exp_string("Successfully created \"shukkie's new repo.git\"")?; + Ok(()) +} |