diff options
-rw-r--r-- | Cargo.lock | 92 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | readme.org | 6 | ||||
-rw-r--r-- | src/main.rs | 50 | ||||
-rw-r--r-- | src/parser.rs | 4 |
5 files changed, 127 insertions, 26 deletions
@@ -108,6 +108,17 @@ dependencies = [ ] [[package]] +name = "clipboard-win" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +dependencies = [ + "error-code", + "str-buf", + "winapi", +] + +[[package]] name = "comma" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -153,6 +164,16 @@ dependencies = [ ] [[package]] +name = "error-code" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +dependencies = [ + "libc", + "str-buf", +] + +[[package]] name = "fastrand" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -341,6 +362,18 @@ dependencies = [ ] [[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "static_assertions", +] + +[[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -488,7 +521,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01ff60778f96fb5a48adbe421d21bf6578ed58c0872d712e7e08593c195adff8" dependencies = [ "comma", - "nix", + "nix 0.25.1", "regex", "tempfile", "thiserror", @@ -509,6 +542,32 @@ dependencies = [ ] [[package]] +name = "rustyline" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfc8644681285d1fb67a467fb3021bfea306b99b4146b166a1fe3ada965eece" +dependencies = [ + "bitflags", + "cfg-if", + "clipboard-win", + "libc", + "log", + "memchr", + "nix 0.26.2", + "scopeguard", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] name = "serde" version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -526,6 +585,7 @@ dependencies = [ "nom", "once_cell", "rexpect", + "rustyline", "shlex", "tempfile", "thiserror", @@ -538,6 +598,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str-buf" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" + +[[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -639,6 +711,18 @@ dependencies = [ ] [[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] name = "url" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -650,6 +734,12 @@ dependencies = [ ] [[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] name = "vcpkg" version = "0.2.15" 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" +rustyline = { version = "11.0.0", default-features = false } shlex = "1.1.0" thiserror = "1.0.38" @@ -21,11 +21,11 @@ Pijul. - [X] Isolation of workdir between tests - [X] git fetch with git upload-pack - [X] git push with git receive-pack -- [X] proper shell argument lexing, with quote stuff https://lib.rs/crates/shlex +- [X] proper shell argument lexing, with quote stuff +- [X] history (only within same session) - [ ] restrict repos to only acceptable paths - [ ] git init of shared repos -- [ ] history (only within same session) https://lib.rs/crates/rustyline -- [ ] don't quit interactive shell sessions if there's an error +- [X] don't quit interactive shell sessions if there's an error - [ ] git archive with git upload-archive - [X] help command - [ ] help docs on all the commands diff --git a/src/main.rs b/src/main.rs index bcbfe1c..e02be3e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,8 @@ mod parser; use clap::Parser; use parser::*; -use std::{io, io::Write, ops::ControlFlow, process::Command}; +use rustyline::{error::ReadlineError, DefaultEditor}; +use std::{io, ops::ControlFlow, process::Command}; use thiserror::Error; /// Shackle Shell - A replacement for git-shell with repo management commands built in. @@ -15,18 +16,6 @@ struct Args { command: Option<String>, } -fn prompt() -> Result<(), ShackleError> { - print!("> "); - io::stdout().flush()?; - Ok(()) -} - -fn read_stdin() -> Result<String, ShackleError> { - let mut buffer = String::new(); - io::stdin().read_line(&mut buffer)?; - Ok(buffer) -} - fn main() -> Result<(), ShackleError> { let args = Args::parse(); match args.command { @@ -95,13 +84,34 @@ fn run_command(user_input: String) -> Result<ControlFlow<(), ()>, ShackleError> } fn run_interactive_loop() -> Result<(), ShackleError> { + let mut rl = DefaultEditor::new()?; 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; + let readline = rl.readline("> "); + match readline { + Ok(user_input) => { + rl.add_history_entry(user_input.as_str())?; + match run_command(user_input) { + Ok(control_flow) => { + if control_flow.is_break() { + break; + } + } + Err(e) => { + println!("{:?}", e); + } + } + } + Err(ReadlineError::Interrupted) => { + println!("Interrupted"); + break; + } + Err(ReadlineError::Eof) => { + break; + } + Err(err) => { + println!("Error: {:?}", err); + break; + } } } Ok(()) @@ -115,4 +125,6 @@ pub enum ShackleError { IoError(#[from] io::Error), #[error(transparent)] GitError(#[from] git2::Error), + #[error(transparent)] + ReadlineError(#[from] ReadlineError), } diff --git a/src/parser.rs b/src/parser.rs index 89e0d76..9938924 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -58,9 +58,7 @@ impl FromStr for ShackleCommand { fn from_str(s: &str) -> Result<Self, Self::Err> { let trimmed = s.trim(); - if s.len() == 0 { - Ok(ShackleCommand::Exit) - } else if trimmed.len() == 0 { + if trimmed.len() == 0 { Ok(ShackleCommand::Whitespace) } else { let lexed = shlex::split(trimmed); |