@ 261104.1 · fetching

260915.1 · 24198d904a074f2943b2a3be925e53134c994734

fix: Better sanitizing

This refactors the sanitization code somewhat, to provide a couple benefits:

- Ensure we sanitize output in error messages (#2119), while not
escaping newlines from error messages.
- Avoids some unnecessary allocations and string copies
- Makes `print_error` a little nicer to use. As a macro, it avoids
needing a separate call to `format!`.

The main downside is added complexity.

Fixes: #2119

reading declarations

Changes to src/error.rs. All files

src/error.rs

@@ -1,3 +1,51 @@-pub fn print_error(msg: impl std::fmt::Display) {-    eprintln!("[fd error]: {msg}");+use std::fmt::Display;+use std::io;++use crate::sanitize::write_sanitized;++/// Print an error message using a format string, sanitizing any arguments if necessary+macro_rules! print_error {+    ($fmt:literal, $($arg:expr),*) => {+        eprintln!(concat!("[fd error]: ", $fmt), $($crate::error::SanitizeErr::sanitize($arg)),*)+    }+}++/// Trait for specifying how to sanitize a type for error display, if needed.+pub(crate) trait SanitizeErr {+    type Sanitized: Display;++    fn sanitize(self) -> Self::Sanitized;+}++impl SanitizeErr for &str {+    type Sanitized = Self;++    fn sanitize(self) -> Self {+        self+    }+}++impl SanitizeErr for io::Error {+    type Sanitized = SanitizedError<io::Error>;++    fn sanitize(self) -> Self::Sanitized {+        SanitizedError(self)+    }+}++impl SanitizeErr for ignore::Error {+    type Sanitized = SanitizedError<ignore::Error>;++    fn sanitize(self) -> Self::Sanitized {+        SanitizedError(self)+    }+}++pub struct SanitizedError<E>(E);++impl<E: std::error::Error> Display for SanitizedError<E> {+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {+        let raw = self.0.to_string();+        write_sanitized(f, &raw)+    } }