feat: Support '-' for stdin

This helps with tool integration.

Fixes #195
This commit is contained in:
Ed Page 2021-01-02 22:17:00 -06:00
parent ed58935d02
commit 1c4d2ac32b
2 changed files with 51 additions and 16 deletions

View file

@ -1,5 +1,7 @@
use bstr::ByteSlice; use bstr::ByteSlice;
use encoding::Encoding; use encoding::Encoding;
use std::io::Read;
use std::io::Write;
use crate::report; use crate::report;
use typos::tokens; use typos::tokens;
@ -207,7 +209,7 @@ impl FileChecker for FixTypos {
reporter.report(msg.into())?; reporter.report(msg.into())?;
} }
} }
if !fixes.is_empty() { if !fixes.is_empty() || path == std::path::Path::new("-") {
let buffer = fix_buffer(buffer, fixes.into_iter()); let buffer = fix_buffer(buffer, fixes.into_iter());
write_file(path, content_type, buffer, reporter)?; write_file(path, content_type, buffer, reporter)?;
} }
@ -504,7 +506,13 @@ pub fn read_file(
path: &std::path::Path, path: &std::path::Path,
reporter: &dyn report::Report, reporter: &dyn report::Report,
) -> Result<(Vec<u8>, content_inspector::ContentType), std::io::Error> { ) -> Result<(Vec<u8>, content_inspector::ContentType), std::io::Error> {
let buffer = report_error(std::fs::read(path), reporter)?; let buffer = if path == std::path::Path::new("-") {
let mut buffer = Vec::new();
report_result(std::io::stdin().read_to_end(&mut buffer), reporter)?;
buffer
} else {
report_result(std::fs::read(path), reporter)?
};
let content_type = content_inspector::inspect(&buffer); let content_type = content_inspector::inspect(&buffer);
@ -520,11 +528,11 @@ pub fn read_file(
(buffer, content_type) (buffer, content_type)
}, },
content_inspector::ContentType::UTF_16LE => { content_inspector::ContentType::UTF_16LE => {
let buffer = report_error(encoding::all::UTF_16LE.decode(&buffer, encoding::DecoderTrap::Strict), reporter)?; let buffer = report_result(encoding::all::UTF_16LE.decode(&buffer, encoding::DecoderTrap::Strict), reporter)?;
(buffer.into_bytes(), content_type) (buffer.into_bytes(), content_type)
} }
content_inspector::ContentType::UTF_16BE => { content_inspector::ContentType::UTF_16BE => {
let buffer = report_error(encoding::all::UTF_16BE.decode(&buffer, encoding::DecoderTrap::Strict), reporter)?; let buffer = report_result(encoding::all::UTF_16BE.decode(&buffer, encoding::DecoderTrap::Strict), reporter)?;
(buffer.into_bytes(), content_type) (buffer.into_bytes(), content_type)
}, },
}; };
@ -547,49 +555,58 @@ pub fn write_file(
| content_inspector::ContentType::UTF_8 | content_inspector::ContentType::UTF_8
| content_inspector::ContentType::UTF_8_BOM => buffer, | content_inspector::ContentType::UTF_8_BOM => buffer,
content_inspector::ContentType::UTF_16LE => { content_inspector::ContentType::UTF_16LE => {
let buffer = report_error(String::from_utf8(buffer), reporter)?; let buffer = report_result(String::from_utf8(buffer), reporter)?;
if buffer.is_empty() { if buffer.is_empty() {
// Error occurred, don't clear out the file // Error occurred, don't clear out the file
return Ok(()); return Ok(());
} }
report_error( report_result(
encoding::all::UTF_16LE.encode(&buffer, encoding::EncoderTrap::Strict), encoding::all::UTF_16LE.encode(&buffer, encoding::EncoderTrap::Strict),
reporter, reporter,
)? )?
} }
content_inspector::ContentType::UTF_16BE => { content_inspector::ContentType::UTF_16BE => {
let buffer = report_error(String::from_utf8(buffer), reporter)?; let buffer = report_result(String::from_utf8(buffer), reporter)?;
if buffer.is_empty() { if buffer.is_empty() {
// Error occurred, don't clear out the file // Error occurred, don't clear out the file
return Ok(()); return Ok(());
} }
report_error( report_result(
encoding::all::UTF_16BE.encode(&buffer, encoding::EncoderTrap::Strict), encoding::all::UTF_16BE.encode(&buffer, encoding::EncoderTrap::Strict),
reporter, reporter,
)? )?
} }
}; };
report_error(std::fs::write(path, buffer), reporter)?; if path == std::path::Path::new("-") {
report_result(std::io::stdout().write_all(&buffer), reporter)?;
} else {
report_result(std::fs::write(path, buffer), reporter)?;
}
Ok(()) Ok(())
} }
fn report_error<T: Default, E: ToString>( fn report_result<T: Default, E: ToString>(
value: Result<T, E>, value: Result<T, E>,
reporter: &dyn report::Report, reporter: &dyn report::Report,
) -> Result<T, std::io::Error> { ) -> Result<T, std::io::Error> {
let buffer = match value { let buffer = match value {
Ok(value) => value, Ok(value) => value,
Err(err) => { Err(err) => {
let msg = report::Error::new(err.to_string()); report_error(err, reporter)?;
reporter.report(msg.into())?;
Default::default() Default::default()
} }
}; };
Ok(buffer) Ok(buffer)
} }
fn report_error<E: ToString>(err: E, reporter: &dyn report::Report) -> Result<(), std::io::Error> {
let msg = report::Error::new(err.to_string());
reporter.report(msg.into())?;
Ok(())
}
struct AccumulateLineNum { struct AccumulateLineNum {
line_num: usize, line_num: usize,
last_offset: usize, last_offset: usize,
@ -697,10 +714,21 @@ fn walk_entry(
dictionary: &dyn typos::Dictionary, dictionary: &dyn typos::Dictionary,
reporter: &dyn report::Report, reporter: &dyn report::Report,
) -> Result<(), ignore::Error> { ) -> Result<(), ignore::Error> {
let entry = entry?; let entry = match entry {
Ok(entry) => entry,
Err(err) => {
report_error(err, reporter)?;
return Ok(());
}
};
if entry.file_type().map(|t| t.is_file()).unwrap_or(true) { if entry.file_type().map(|t| t.is_file()).unwrap_or(true) {
let explicit = entry.depth() == 0; let explicit = entry.depth() == 0;
checks.check_file(entry.path(), explicit, parser, dictionary, reporter)?; let path = if entry.is_stdin() {
std::path::Path::new("-")
} else {
entry.path()
};
checks.check_file(path, explicit, parser, dictionary, reporter)?;
} }
Ok(()) Ok(())

View file

@ -34,6 +34,7 @@ fn run() -> proc_exit::ExitResult {
}; };
init_logging(args.verbose.log_level()); init_logging(args.verbose.log_level());
let global_cwd = std::env::current_dir()?;
let config = if let Some(path) = args.custom_config.as_ref() { let config = if let Some(path) = args.custom_config.as_ref() {
config::Config::from_file(path).with_code(proc_exit::Code::CONFIG_ERR)? config::Config::from_file(path).with_code(proc_exit::Code::CONFIG_ERR)?
@ -44,8 +45,14 @@ fn run() -> proc_exit::ExitResult {
let mut typos_found = false; let mut typos_found = false;
let mut errors_found = false; let mut errors_found = false;
for path in args.path.iter() { for path in args.path.iter() {
let path = path.canonicalize().with_code(proc_exit::Code::USAGE_ERR)?; let path = if path == std::path::Path::new("-") {
let cwd = if path.is_file() { path.to_owned()
} else {
path.canonicalize().with_code(proc_exit::Code::USAGE_ERR)?
};
let cwd = if path == std::path::Path::new("-") {
global_cwd.as_path()
} else if path.is_file() {
path.parent().unwrap() path.parent().unwrap()
} else { } else {
path.as_path() path.as_path()