Skip to content

Add methods to get access to the wrapped Write reference.#17

Open
ilyvion wants to merge 1 commit intoeyre-rs:masterfrom
ilyvion-contrib:master
Open

Add methods to get access to the wrapped Write reference.#17
ilyvion wants to merge 1 commit intoeyre-rs:masterfrom
ilyvion-contrib:master

Conversation

@ilyvion
Copy link
Copy Markdown

@ilyvion ilyvion commented Jul 13, 2022

I'm using the CodeFormatter with a small shim that forwards writes to its fmt::Write implementation to its wrapped io::Write, as I want to write formatted code directly to the io::Write stream rather than writing to a byte or string buffer first. It looks like this:

use std::fmt::{Error as FmtError, Result as FmtResult, Write as FmtWrite};
use std::io::{Error as IoError, Write as IoWrite};

struct FmtWriter<W: IoWrite>(W, Option<IoError>);
impl<W: IoWrite> FmtWrite for FmtWriter<W> {
    fn write_str(&mut self, s: &str) -> FmtResult {
        self.0.write_all(s.as_bytes()).map_err(|e| {
            self.1 = Some(e);
            FmtError
        })
    }

    fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> FmtResult {
        self.0.write_fmt(args).map_err(|e| {
            self.1 = Some(e);
            FmtError
        })
    }
}

Because fmt::Error is only used as a "flag," it doesn't contain any information about what caused the error, as the documentation also points out:

This type does not support transmission of an error other than that an error occurred. Any extra information must be arranged to be transmitted through some other means.

Consequently, my FmtWriter has a second field, Option<io::Error>, which stores the real error that happened, before passing on an fmt::Error as the fmt::Write expects.

The way I've worked with this until now, is as follows:

// Make our FmtWriter
let mut fmt_writer = FmtWriter(output_writer, None);
// Wrap it with the CodeFormatter
let mut formatter = CodeFormatter::new(&mut fmt_writer, "    ");
// [...]
if write!(
    formatter,
    r#"
    ... code goes here ...
    "#,
).is_err() {
    // If an error occurs, take() the actual IO error out of the FmtWriter and return it
    return Err(fmt_writer.1.take().unwrap())?;
}

This works fine when the fmt_writer and formatter are used where they are created, but fall apart if you want to start refactoring this code into smaller functions, because now formatter has a &mut borrow of fmt_writer, so you can't pass them both at the same time.

With my changes, I only need to pass along a &mut formatter to the smaller functions, and then I can handle errors like this instead:

return Err(formatter.inner_mut().1.take().unwrap())?;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant