Skip to content

Commit 06fc4ac

Browse files
committed
chore: add --stdin-paths and -stdin-paths0
1 parent 66d82e5 commit 06fc4ac

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

crates/typos-cli/src/bin/typos-cli/args.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ pub(crate) struct Args {
5050
#[arg(default_value = ".")]
5151
pub(crate) path: Vec<std::path::PathBuf>,
5252

53+
/// Read the list of newline separated paths from stdin
54+
#[arg(long, conflicts_with = "stdin_paths0")]
55+
pub(crate) stdin_paths: bool,
56+
57+
/// Read the list of '\0' separated paths from stdin
58+
#[arg(long, conflicts_with = "stdin_paths")]
59+
pub(crate) stdin_paths0: bool,
60+
5361
/// Custom config file
5462
#[arg(short = 'c', long = "config")]
5563
pub(crate) custom_config: Option<std::path::PathBuf>,

crates/typos-cli/src/bin/typos-cli/main.rs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use std::io::Write;
1+
use std::{
2+
io::{self, Read, Write},
3+
iter,
4+
path::PathBuf,
5+
};
26

37
use clap::Parser;
48

@@ -156,7 +160,20 @@ fn run_checks(args: &args::Args) -> proc_exit::ExitResult {
156160

157161
let mut typos_found = false;
158162
let mut errors_found = false;
159-
for path in args.path.iter() {
163+
164+
let paths_from_stdin = if args.stdin_paths {
165+
io::stdin()
166+
.lines()
167+
.map(|res| res.map(PathBuf::from))
168+
.collect::<Result<_, _>>()
169+
.with_code(proc_exit::sysexits::IO_ERR)?
170+
} else if args.stdin_paths0 {
171+
unix_read_paths_from_stdin()?
172+
} else {
173+
vec![]
174+
};
175+
176+
for path in args.path.iter().chain(&paths_from_stdin) {
160177
let cwd = if path == std::path::Path::new("-") {
161178
global_cwd.clone()
162179
} else if path.is_file() {
@@ -269,6 +286,46 @@ fn run_checks(args: &args::Args) -> proc_exit::ExitResult {
269286
}
270287
}
271288

289+
#[cfg(target_family = "unix")]
290+
fn unix_read_paths_from_stdin() -> Result<Vec<PathBuf>, proc_exit::Exit> {
291+
use std::ffi::OsString;
292+
use std::os::unix::ffi::OsStringExt;
293+
294+
let mut buf = Vec::new();
295+
io::stdin()
296+
.read_to_end(&mut buf)
297+
.with_code(proc_exit::sysexits::IO_ERR)?;
298+
299+
Ok(buf
300+
.into_iter()
301+
// ensure we always end with a terminator
302+
.chain(iter::once(0))
303+
.fold(
304+
(vec![], vec![]),
305+
|(mut paths, mut cur_path): (Vec<PathBuf>, Vec<u8>), byte| {
306+
if byte == 0 {
307+
if cur_path.is_empty() {
308+
// ignore duplicated \0
309+
// TODO: is this OK? If not, we could not append trailing 0, and handle "rest" after `fold` ends
310+
(paths, cur_path)
311+
} else {
312+
paths.push(PathBuf::from(OsString::from_vec(cur_path)));
313+
(paths, vec![])
314+
}
315+
} else {
316+
cur_path.push(byte);
317+
(paths, cur_path)
318+
}
319+
},
320+
)
321+
.0)
322+
}
323+
324+
#[cfg(not(target_family = "unix"))]
325+
fn unix_read_paths_from_stdin() -> Result<Vec<PathBuf>, proc_exit::Exit> {
326+
Err(proc_exit::sysexits::CONFIG_ERR)
327+
}
328+
272329
fn init_logging(level: Option<log::Level>) {
273330
if let Some(level) = level {
274331
let mut builder = env_logger::Builder::new();

0 commit comments

Comments
 (0)