diff --git a/crates/typos-cli/src/config.rs b/crates/typos-cli/src/config.rs index 5eb5d5c88..67701a9db 100644 --- a/crates/typos-cli/src/config.rs +++ b/crates/typos-cli/src/config.rs @@ -31,7 +31,13 @@ impl Config { } pub fn from_file(path: &std::path::Path) -> Result { - let s = std::fs::read_to_string(path)?; + let s = std::fs::read_to_string(path).map_err(|err| { + let kind = err.kind(); + std::io::Error::new( + kind, + format!("could not read config at `{}`", path.display()), + ) + })?; Self::from_toml(&s) } diff --git a/crates/typos-cli/src/file.rs b/crates/typos-cli/src/file.rs index 16767e2ac..07e14d1c4 100644 --- a/crates/typos-cli/src/file.rs +++ b/crates/typos-cli/src/file.rs @@ -431,10 +431,14 @@ fn read_file( ) -> Result<(Vec, content_inspector::ContentType), std::io::Error> { let buffer = if path == std::path::Path::new("-") { let mut buffer = Vec::new(); - report_result(std::io::stdin().read_to_end(&mut buffer), reporter)?; + report_result( + std::io::stdin().read_to_end(&mut buffer), + Some(path), + reporter, + )?; buffer } else { - report_result(std::fs::read(path), reporter)? + report_result(std::fs::read(path), Some(path), reporter)? }; let content_type = content_inspector::inspect(&buffer); @@ -460,7 +464,7 @@ fn read_file( encoding_rs::DecoderResult::InputEmpty => Ok(decoded), _ => Err(format!("invalid UTF-16LE encoding at byte {} in {}", written, path.display())), }; - let buffer = report_result(decoded, reporter)?; + let buffer = report_result(decoded, Some(path), reporter)?; (buffer.into_bytes(), content_type) } content_inspector::ContentType::UTF_16BE => { @@ -473,7 +477,7 @@ fn read_file( encoding_rs::DecoderResult::InputEmpty => Ok(decoded), _ => Err(format!("invalid UTF-16BE encoding at byte {} in {}", written, path.display())), }; - let buffer = report_result(decoded, reporter)?; + let buffer = report_result(decoded, Some(path), reporter)?; (buffer.into_bytes(), content_type) }, }; @@ -496,7 +500,7 @@ fn write_file( | content_inspector::ContentType::UTF_8 | content_inspector::ContentType::UTF_8_BOM => buffer, content_inspector::ContentType::UTF_16LE => { - let buffer = report_result(String::from_utf8(buffer), reporter)?; + let buffer = report_result(String::from_utf8(buffer), Some(path), reporter)?; if buffer.is_empty() { // Error occurred, don't clear out the file return Ok(()); @@ -509,7 +513,7 @@ fn write_file( encoded.into_owned() } content_inspector::ContentType::UTF_16BE => { - let buffer = report_result(String::from_utf8(buffer), reporter)?; + let buffer = report_result(String::from_utf8(buffer), Some(path), reporter)?; if buffer.is_empty() { // Error occurred, don't clear out the file return Ok(()); @@ -524,9 +528,9 @@ fn write_file( }; if path == std::path::Path::new("-") { - report_result(std::io::stdout().write_all(&buffer), reporter)?; + report_result(std::io::stdout().write_all(&buffer), Some(path), reporter)?; } else { - report_result(std::fs::write(path, buffer), reporter)?; + report_result(std::fs::write(path, buffer), Some(path), reporter)?; } Ok(()) @@ -547,20 +551,26 @@ fn check_bytes<'a>( fn report_result( value: Result, + path: Option<&std::path::Path>, reporter: &dyn report::Report, ) -> Result { let buffer = match value { Ok(value) => value, Err(err) => { - report_error(err, reporter)?; + report_error(err, path, reporter)?; Default::default() } }; Ok(buffer) } -fn report_error(err: E, reporter: &dyn report::Report) -> Result<(), std::io::Error> { - let msg = report::Error::new(err.to_string()); +fn report_error( + err: E, + path: Option<&std::path::Path>, + reporter: &dyn report::Report, +) -> Result<(), std::io::Error> { + let mut msg = report::Error::new(err.to_string()); + msg.context = path.map(|path| report::Context::Path(report::PathContext { path })); reporter.report(msg.into())?; Ok(()) } @@ -672,7 +682,7 @@ fn walk_entry( let entry = match entry { Ok(entry) => entry, Err(err) => { - report_error(err, reporter)?; + report_error(err, None, reporter)?; return Ok(()); } }; @@ -686,10 +696,15 @@ fn walk_entry( let explicit = entry.depth() == 0; let (path, lookup_path) = if entry.is_stdin() { let path = std::path::Path::new("-"); - (path, std::env::current_dir()?) + let cwd = std::env::current_dir().map_err(|err| { + let kind = err.kind(); + std::io::Error::new(kind, "no current working directory".to_owned()) + })?; + (path, cwd) } else { let path = entry.path(); - (path, path.canonicalize()?) + let abs_path = report_result(path.canonicalize(), Some(path), reporter)?; + (path, abs_path) }; let policy = engine.policy(&lookup_path); checks.check_file(path, explicit, &policy, reporter)?; diff --git a/crates/typos-cli/tests/cmd/missing-config.in/.keep b/crates/typos-cli/tests/cmd/missing-config.in/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/crates/typos-cli/tests/cmd/missing-config.toml b/crates/typos-cli/tests/cmd/missing-config.toml new file mode 100644 index 000000000..e3232b649 --- /dev/null +++ b/crates/typos-cli/tests/cmd/missing-config.toml @@ -0,0 +1,8 @@ +bin.name = "typos" +args = "--config foo.toml" +status.code = 78 +stdin = "" +# stdout doesn't have stable order +stderr = """ +could not read config at `foo.toml` +"""