Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ilyvion
Copy link

@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