From abc4367cdda929c906a1a58da35603797b23a6bd Mon Sep 17 00:00:00 2001 From: Austen Adler Date: Tue, 27 Dec 2022 19:58:55 -0500 Subject: [PATCH] Add clap --- .drone.yml | 8 ++- Cargo.lock | 180 ++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/command.rs | 22 +++--- src/main.rs | 47 ++++++++----- src/ui.rs | 41 ++++++++--- 6 files changed, 259 insertions(+), 40 deletions(-) diff --git a/.drone.yml b/.drone.yml index e45edb0..07e1d15 100644 --- a/.drone.yml +++ b/.drone.yml @@ -16,14 +16,16 @@ steps: - 'file /var/run/docker.sock || :' - 'pwd' - 'earthly +all' - # TODO: use more than one target - # - 'earthly --build-arg TOOLCHAIN=x86_64-unknown-linux-musl --build-arg STRIP_CMD=x86_64-linux-gnu-strip +build' - 'cp -v ./dist/* /dist/' - name: Gitea Artifacts image: plugins/gitea-release + when: + event: + - tag settings: - api_key: "$${GITEA_API_KEY}" + api_key: + from_secret: GITEA_API_KEY base_url: https://gitea.austen-wares.com files: dist/* volumes: diff --git a/Cargo.lock b/Cargo.lock index 7619a82..6aaa0cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,7 +32,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -70,6 +70,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" +[[package]] +name = "cc" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" + [[package]] name = "cfg-if" version = "0.1.10" @@ -82,6 +88,43 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cpufeatures" version = "0.2.5" @@ -213,6 +256,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "filedescriptor" version = "0.8.2" @@ -262,6 +326,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -271,12 +341,43 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "io-lifetimes" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -289,13 +390,20 @@ version = "0.2.138" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "live-cli" -version = "0.1.0" +version = "0.2.0" dependencies = [ "ansi4tui", "anyhow", "atty", + "clap", "crossbeam", "crossterm", "parking_lot", @@ -419,6 +527,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + [[package]] name = "overload" version = "0.1.1" @@ -508,6 +622,30 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.47" @@ -624,6 +762,20 @@ dependencies = [ "str_indices", ] +[[package]] +name = "rustix" +version = "0.36.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -738,6 +890,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f026164926842ec52deb1938fae44f83dfdb82d0a5b0270c5bd5935ab74d6dd" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "1.0.105" @@ -749,6 +907,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "terminfo" version = "0.7.3" @@ -988,6 +1155,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 05f148c..b3f5e95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ parking_lot = "0.12.1" tracing = { version = "0.1.37", features = ["release_max_level_off"] } tracing-subscriber = "0.3.16" ropey = "1.5.0" +clap = { version = "4.0.32", features = ["derive"] } [package.metadata.deb] maintainer = "Austen Adler " diff --git a/src/command.rs b/src/command.rs index 63d4055..88629a0 100644 --- a/src/command.rs +++ b/src/command.rs @@ -13,7 +13,7 @@ use std::{ process::{Command, Stdio}, }; -pub type CommandRequest = (Arc>, Arc); +pub type CommandRequest = (Arc>, Arc>); #[derive(Debug)] pub enum CommandCompleted { @@ -34,8 +34,8 @@ pub fn command_event_loop( ) -> Result<()> { loop { match command_request_receiver.recv() { - // TODO: Drain the command request channel so we only run the command once with the latest value - // Could use https://docs.rs/single_value_channel/latest/single_value_channel/ + // TODO: Drain the command request channel so we only run the command once with the latest value + // Could use https://docs.rs/single_value_channel/latest/single_value_channel/ Ok(command_request) => { event_sender.send(EventMessage::CommandCompleted( match run_inner(&command_request) { @@ -66,7 +66,6 @@ fn run_inner(command_request: &CommandRequest) -> Result { let mut command = Command::new(&request.command); command - .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .args(&request.hidden_options); @@ -85,16 +84,21 @@ fn run_inner(command_request: &CommandRequest) -> Result { command.arg(&request.cmdline); } + command.stdin(Stdio::piped()); + std::mem::drop(request); command.spawn() }?; - let mut stdin = child.stdin.take().context("Could not take stdin")?; - let text_orig_clone = command_request.1.clone(); - std::thread::spawn(move || { - let _result = stdin.write_all(text_orig_clone.as_bytes()); - }); + if command_request.1.is_some() { + let mut stdin = child.stdin.take().context("Could not take stdin")?; + let text_orig_clone = command_request.1.clone(); + + std::thread::spawn(move || { + let _result = stdin.write_all(text_orig_clone.as_ref().as_ref().unwrap().as_bytes()); + }); + } // Collect the output let output = child.wait_with_output()?; diff --git a/src/main.rs b/src/main.rs index d78bc03..268cc56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,9 @@ mod ui; mod util; use anyhow::anyhow; use anyhow::bail; +use anyhow::Context; use anyhow::Result; +use clap::Parser; use command::CommandCompleted; use command::CommandRequest; use command::CommandResult; @@ -36,9 +38,9 @@ use crossterm::{ terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; +// Tracing only #[cfg(debug_assertions)] use { - // Tracing std::fs::File, tracing::instrument, tracing::Level, @@ -46,6 +48,15 @@ use { tracing_subscriber::{filter, prelude::*}, }; +#[derive(Parser)] +pub struct AppArgs { + #[clap(short, long, help = "Slurp stdin as input")] + stdin: bool, + + #[clap(default_value = "sh")] + command: String, +} + /// The state of options for the command pub struct CommandOptions { /// The actual command to be called @@ -73,10 +84,10 @@ pub struct CommandOptions { /// The state of the application pub struct App { /// Original text - text_orig: Arc, + text_orig: Arc>, /// Original text (for ui) - text_orig_rope: Rope, + text_orig_rope: Option, // text_orig_formatted: Arc /// The list of options for a command, given in an RwLock so the command runner can update it @@ -151,10 +162,11 @@ impl App { pub fn from_template( message_rx: Receiver, command_request_tx: Sender, - input: String, + input: Option, template: &Template, ) -> Self { - let text_orig_rope = Rope::from_str(&input); + // let text_orig_rope = Rope::from_str(&input); + let text_orig_rope = input.as_ref().map(|s| Rope::from_str(s)); Self { text_orig: Arc::new(input), text_orig_rope, @@ -222,19 +234,19 @@ fn main() -> Result<()> { #[cfg(debug_assertions)] enable_tracing(); - // Error if we aren't getting any stdin - if atty::is(atty::Stream::Stdin) { - bail!("You must send stdin to this command"); - } - - // Get the arguments - let arg = match std::env::args().nth(1) { - Some(a) => a, - None => bail!("You must pass a command"), - }; + let args = AppArgs::parse(); // Slurp all input - let text_orig = io::read_to_string(io::stdin())?; + let text_orig = match (args.stdin, atty::is(atty::Stream::Stdin)) { + // The requested stdin, but it's a tty + (true, true) => bail!("Stdin was requested with, but stdin is a tty"), + + // They did not request stdin, and they didn't pipe to us + (false, true) => None, + + // Someone is piping us something + (_, false) => Some(io::read_to_string(io::stdin()).context("Could not read stdin")?), + }; // Start the command worker thread let (message_rx, command_request_tx) = init_message_passing(); @@ -244,11 +256,10 @@ fn main() -> Result<()> { message_rx, command_request_tx, text_orig, - &Template::from_str(&arg)?, + &Template::from_str(&args.command)?, ); enable_raw_mode()?; let mut stdout = io::stdout(); - // execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; execute!( stdout, EnterAlternateScreen, diff --git a/src/ui.rs b/src/ui.rs index 31df110..97e1888 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -9,7 +9,7 @@ use tracing::instrument; use tracing::Level; use tui::{ backend::Backend, - layout::{Constraint, Direction, Layout, Rect}, + layout::{Alignment, Constraint, Direction, Layout, Rect}, widgets::{Block, Borders, Paragraph}, Frame, }; @@ -32,13 +32,38 @@ pub fn draw(f: &mut Frame, app: &App) { event!(Level::INFO, "Rendering orig"); // lazy_render_rope_slice(chunks[0], render_states.stdout) - render_states.stdout = lazy_render_rope_slice( - f, - chunks[0], - render_states.stdin.as_ref(), - app.text_orig_rope.slice(..), - "Output", - ); + render_states.stdout = if let Some(text_orig_rope) = app.text_orig_rope.as_ref() { + lazy_render_rope_slice( + f, + chunks[0], + render_states.stdin.as_ref(), + text_orig_rope.slice(..), + "Output", + ) + } else { + f.render_widget( + Paragraph::new("! No Stdin !") + .alignment(Alignment::Center) + .block(Block::default().title("Output").borders(Borders::ALL)), + chunks[0], // Layout::default() + // .direction(Direction::Vertical) + // .constraints( + // [ + // Constraint::Percentage(50), + // Constraint::Length(1), + // Constraint::Min(0), + // ] + // .as_ref(), + // ) + // .split(chunks[0])[1], + ); + Some(RenderState { + // TODO + size: chunks[0], + input_hash: 0, + }) + }; + event!(Level::INFO, "Rendering textbox"); f.render_widget( Paragraph::new(command_options.cmdline.as_ref()).block(