From c66dc5693ad2c40786442b2f25902e1efdfd5f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Ci=C4=99=C5=BCarkiewicz?= Date: Thu, 6 Apr 2023 16:39:21 -0700 Subject: [PATCH] chore: add `--stdin-paths` and `-stdin-paths0` --- Cargo.lock | 4 ++ crates/typos-cli/Cargo.toml | 1 + crates/typos-cli/src/bin/typos-cli/args.rs | 16 ++++- crates/typos-cli/src/bin/typos-cli/main.rs | 69 ++++++++++++++++++- crates/typos-cli/tests/cli_tests.rs | 4 +- .../tests/cmd/stdin-paths.in/_typos.toml | 8 +++ .../typos-cli/tests/cmd/stdin-paths.in/a.fail | 1 + .../typos-cli/tests/cmd/stdin-paths.in/b.fail | 1 + .../typos-cli/tests/cmd/stdin-paths.in/c.fail | 1 + .../typos-cli/tests/cmd/stdin-paths.in/d.fail | 1 + crates/typos-cli/tests/cmd/stdin-paths.toml | 22 ++++++ .../tests/cmd/stdin-paths0.in/_typos.toml | 8 +++ .../tests/cmd/stdin-paths0.in/a.fail | 1 + .../tests/cmd/stdin-paths0.in/b.fail | 1 + .../tests/cmd/stdin-paths0.in/c.fail | 1 + .../tests/cmd/stdin-paths0.in/d.fail | 1 + crates/typos-cli/tests/cmd/stdin-paths0.toml | 19 +++++ 17 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 crates/typos-cli/tests/cmd/stdin-paths.in/_typos.toml create mode 100644 crates/typos-cli/tests/cmd/stdin-paths.in/a.fail create mode 100644 crates/typos-cli/tests/cmd/stdin-paths.in/b.fail create mode 100644 crates/typos-cli/tests/cmd/stdin-paths.in/c.fail create mode 100644 crates/typos-cli/tests/cmd/stdin-paths.in/d.fail create mode 100644 crates/typos-cli/tests/cmd/stdin-paths.toml create mode 100644 crates/typos-cli/tests/cmd/stdin-paths0.in/_typos.toml create mode 100644 crates/typos-cli/tests/cmd/stdin-paths0.in/a.fail create mode 100644 crates/typos-cli/tests/cmd/stdin-paths0.in/b.fail create mode 100644 crates/typos-cli/tests/cmd/stdin-paths0.in/c.fail create mode 100644 crates/typos-cli/tests/cmd/stdin-paths0.in/d.fail create mode 100644 crates/typos-cli/tests/cmd/stdin-paths0.toml diff --git a/Cargo.lock b/Cargo.lock index ca5adaf7d..8a0dd5134 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1016,6 +1016,9 @@ name = "os_str_bytes" version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" +dependencies = [ + "memchr", +] [[package]] name = "phf" @@ -1644,6 +1647,7 @@ dependencies = [ "log", "maplit", "once_cell", + "os_str_bytes", "proc-exit", "regex", "serde", diff --git a/crates/typos-cli/Cargo.toml b/crates/typos-cli/Cargo.toml index 5cfcec699..695138c89 100644 --- a/crates/typos-cli/Cargo.toml +++ b/crates/typos-cli/Cargo.toml @@ -78,6 +78,7 @@ colorchoice-clap = "1.0.3" serde_regex = "1.1.0" regex = "1.9.6" encoding_rs = "0.8.33" +os_str_bytes = "6.5.1" [dev-dependencies] assert_fs = "1.0" diff --git a/crates/typos-cli/src/bin/typos-cli/args.rs b/crates/typos-cli/src/bin/typos-cli/args.rs index 6ccbc69c6..20cb3e680 100644 --- a/crates/typos-cli/src/bin/typos-cli/args.rs +++ b/crates/typos-cli/src/bin/typos-cli/args.rs @@ -39,9 +39,17 @@ impl Format { #[command(group = clap::ArgGroup::new("mode").multiple(false))] pub(crate) struct Args { /// Paths to check with `-` for stdin - #[arg(default_value = ".")] + #[arg(default_value = ".", group = "source")] pub(crate) path: Vec, + /// Read the list of newline separated paths from stdin + #[arg(long, group = "source")] + pub(crate) stdin_paths: bool, + + /// Read the list of '\0' separated paths from stdin + #[arg(long, group = "source")] + pub(crate) stdin_paths0: bool, + /// The approximate number of threads to use. #[arg(short = 'j', long = "threads", default_value = "0")] pub(crate) threads: usize, @@ -110,6 +118,12 @@ pub(crate) struct Args { pub(crate) verbose: clap_verbosity_flag::Verbosity, } +impl Args { + pub fn is_using_stdin_paths(&self) -> bool { + self.stdin_paths || self.stdin_paths0 + } +} + #[derive(Debug, Clone, clap::Args)] #[command(rename_all = "kebab-case")] pub(crate) struct FileArgs { diff --git a/crates/typos-cli/src/bin/typos-cli/main.rs b/crates/typos-cli/src/bin/typos-cli/main.rs index 017da7df5..a8e78930c 100644 --- a/crates/typos-cli/src/bin/typos-cli/main.rs +++ b/crates/typos-cli/src/bin/typos-cli/main.rs @@ -1,4 +1,6 @@ -use std::io::Write; +use std::ffi::OsString; +use std::io::{Read as _, Write as _}; +use std::path::PathBuf; use clap::Parser; @@ -167,8 +169,29 @@ fn run_checks(args: &args::Args) -> proc_exit::ExitResult { let mut typos_found = false; let mut errors_found = false; - for path in args.path.iter() { + + let stdin_paths = if args.stdin_paths { + Some( + std::io::stdin() + .lines() + .map(|res| res.map(PathBuf::from)) + .collect::>() + .with_code(proc_exit::sysexits::IO_ERR)?, + ) + } else if args.stdin_paths0 { + Some(unix_read_paths_from_stdin()?) + } else { + None + }; + + // Note: stdin_paths and args.path are mutually exclusive, enforced by clap + for path in stdin_paths.as_ref().unwrap_or(&args.path) { + // Note paths are passed through stdin, `-` is treated like a normal path let cwd = if path == std::path::Path::new("-") { + if args.is_using_stdin_paths() { + return Err(proc_exit::sysexits::USAGE_ERR + .with_message("Can't use `-` (stdin) while using stdin provided paths")); + }; global_cwd.clone() } else if path.is_file() { let mut cwd = path @@ -293,6 +316,48 @@ fn run_checks(args: &args::Args) -> proc_exit::ExitResult { } } +#[cfg(target_family = "unix")] +fn os_str_from_bytes(bytes: Vec) -> Result { + use std::os::unix::ffi::OsStringExt; + + Ok(OsString::from_vec(bytes)) +} + +#[cfg(not(target_family = "unix"))] +fn os_str_from_bytes(bytes: Vec) -> Result { + use os_str_bytes::RawOsString; + + Ok(RawOsString::from_raw_vec(bytes).map_err(|_e| { + proc_exit::sysexits::USAGE_ERR + .with_message("Invalid encoding")? + .into_os_string() + })) +} + +fn unix_read_paths_from_stdin() -> Result, proc_exit::Exit> { + let mut buf = Vec::new(); + std::io::stdin() + .read_to_end(&mut buf) + .with_code(proc_exit::sysexits::IO_ERR)?; + + let (mut paths, rest) = buf.into_iter().try_fold( + (vec![], vec![]), + |(mut paths, mut cur_path): (Vec, Vec), byte| { + if byte == 0 { + paths.push(PathBuf::from(os_str_from_bytes(cur_path)?)); + Ok((paths, vec![])) + } else { + cur_path.push(byte); + Ok((paths, cur_path)) + } + }, + )?; + if !rest.is_empty() { + paths.push(PathBuf::from(os_str_from_bytes(rest)?)); + } + Ok(paths) +} + fn init_logging(level: Option) { if let Some(level) = level { let mut builder = env_logger::Builder::new(); diff --git a/crates/typos-cli/tests/cli_tests.rs b/crates/typos-cli/tests/cli_tests.rs index b81c6d1ff..06a850e1f 100644 --- a/crates/typos-cli/tests/cli_tests.rs +++ b/crates/typos-cli/tests/cli_tests.rs @@ -1,5 +1,7 @@ #[test] #[cfg(feature = "dict")] fn cli_tests() { - trycmd::TestCases::new().case("tests/cmd/*.toml"); + trycmd::TestCases::new() + .case("tests/cmd/*.toml") + .case("tests/cmd/*.trycmd"); } diff --git a/crates/typos-cli/tests/cmd/stdin-paths.in/_typos.toml b/crates/typos-cli/tests/cmd/stdin-paths.in/_typos.toml new file mode 100644 index 000000000..82c777b9e --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths.in/_typos.toml @@ -0,0 +1,8 @@ +[files] +extend-exclude = ["_typos.toml"] + +[default.extend-identifiers] +hello = "goodbye" + +[type.fail] +extend-glob = ["*.fail"] \ No newline at end of file diff --git a/crates/typos-cli/tests/cmd/stdin-paths.in/a.fail b/crates/typos-cli/tests/cmd/stdin-paths.in/a.fail new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths.in/a.fail @@ -0,0 +1 @@ +hello diff --git a/crates/typos-cli/tests/cmd/stdin-paths.in/b.fail b/crates/typos-cli/tests/cmd/stdin-paths.in/b.fail new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths.in/b.fail @@ -0,0 +1 @@ +hello diff --git a/crates/typos-cli/tests/cmd/stdin-paths.in/c.fail b/crates/typos-cli/tests/cmd/stdin-paths.in/c.fail new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths.in/c.fail @@ -0,0 +1 @@ +hello diff --git a/crates/typos-cli/tests/cmd/stdin-paths.in/d.fail b/crates/typos-cli/tests/cmd/stdin-paths.in/d.fail new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths.in/d.fail @@ -0,0 +1 @@ +hello diff --git a/crates/typos-cli/tests/cmd/stdin-paths.toml b/crates/typos-cli/tests/cmd/stdin-paths.toml new file mode 100644 index 000000000..ff17100c0 --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths.toml @@ -0,0 +1,22 @@ +bin.name = "typos" +args = "--stdin-paths" +stdin = """ +b.fail +d.fail +""" +stdout = """ +error: `hello` should be `goodbye` + --> b.fail:1:1 + | +1 | hello + | ^^^^^ + | +error: `hello` should be `goodbye` + --> d.fail:1:1 + | +1 | hello + | ^^^^^ + | +""" +stderr = "" +status.code = 2 diff --git a/crates/typos-cli/tests/cmd/stdin-paths0.in/_typos.toml b/crates/typos-cli/tests/cmd/stdin-paths0.in/_typos.toml new file mode 100644 index 000000000..82c777b9e --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths0.in/_typos.toml @@ -0,0 +1,8 @@ +[files] +extend-exclude = ["_typos.toml"] + +[default.extend-identifiers] +hello = "goodbye" + +[type.fail] +extend-glob = ["*.fail"] \ No newline at end of file diff --git a/crates/typos-cli/tests/cmd/stdin-paths0.in/a.fail b/crates/typos-cli/tests/cmd/stdin-paths0.in/a.fail new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths0.in/a.fail @@ -0,0 +1 @@ +hello diff --git a/crates/typos-cli/tests/cmd/stdin-paths0.in/b.fail b/crates/typos-cli/tests/cmd/stdin-paths0.in/b.fail new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths0.in/b.fail @@ -0,0 +1 @@ +hello diff --git a/crates/typos-cli/tests/cmd/stdin-paths0.in/c.fail b/crates/typos-cli/tests/cmd/stdin-paths0.in/c.fail new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths0.in/c.fail @@ -0,0 +1 @@ +hello diff --git a/crates/typos-cli/tests/cmd/stdin-paths0.in/d.fail b/crates/typos-cli/tests/cmd/stdin-paths0.in/d.fail new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths0.in/d.fail @@ -0,0 +1 @@ +hello diff --git a/crates/typos-cli/tests/cmd/stdin-paths0.toml b/crates/typos-cli/tests/cmd/stdin-paths0.toml new file mode 100644 index 000000000..5bc5f395a --- /dev/null +++ b/crates/typos-cli/tests/cmd/stdin-paths0.toml @@ -0,0 +1,19 @@ +bin.name = "typos" +args = "--stdin-paths0" +stdin = "b.fail\u0000c.fail" +stdout = """ +error: `hello` should be `goodbye` + --> b.fail:1:1 + | +1 | hello + | ^^^^^ + | +error: `hello` should be `goodbye` + --> c.fail:1:1 + | +1 | hello + | ^^^^^ + | +""" +stderr = "" +status.code = 2