diff options
author | Robin Krahl <robin.krahl@ireas.org> | 2019-02-17 13:10:36 +0000 |
---|---|---|
committer | Daniel Mueller <deso@posteo.net> | 2019-02-17 19:50:35 -0800 |
commit | ff3c5fd8f6370265156d308b44c3b039ead12fd4 (patch) | |
tree | 7621d45783cf77b9ef3aeaf49685abf8e85997e1 | |
parent | 41f352bff4d7cb221d009b1bfc7f0664bea9b348 (diff) | |
download | nitrocli-ff3c5fd8f6370265156d308b44c3b039ead12fd4.tar.gz nitrocli-ff3c5fd8f6370265156d308b44c3b039ead12fd4.tar.bz2 |
Use buffered writer in args::parse_arguments
To be able to decide whether to print the argparse output depending on
the result of the argument parsing, this patch wraps stdout and stderr
in a BufWriter before invoking argparse. Our BufWriter implementation
only writes to the inner Write if the flush method is called. This
allows us to decide whether the buffered data should be written or
silently dropped.
-rw-r--r-- | nitrocli/src/args.rs | 53 |
1 files changed, 52 insertions, 1 deletions
diff --git a/nitrocli/src/args.rs b/nitrocli/src/args.rs index a0ad583..924b10e 100644 --- a/nitrocli/src/args.rs +++ b/nitrocli/src/args.rs @@ -29,6 +29,38 @@ use crate::RunCtx; type Result<T> = result::Result<T, Error>; +/// Wraps a writer and buffers its output. +/// +/// This implementation is similar to `io::BufWriter`, but: +/// - The inner writer is only written to if `flush` is called. +/// - The buffer may grow infinitely large. +struct BufWriter<'w, W: io::Write + ?Sized> { + buf: Vec<u8>, + inner: &'w mut W, +} + +impl<'w, W: io::Write + ?Sized> BufWriter<'w, W> { + pub fn new(inner: &'w mut W) -> Self { + BufWriter { + buf: Vec::with_capacity(128), + inner, + } + } +} + +impl<'w, W: io::Write + ?Sized> io::Write for BufWriter<'w, W> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.buf.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.write_all(&self.buf)?; + self.buf.clear(); + self.inner.flush() + } +} + trait Stdio { fn stdio(&mut self) -> (&mut dyn io::Write, &mut dyn io::Write); } @@ -39,6 +71,15 @@ impl<'io> Stdio for RunCtx<'io> { } } +impl<W> Stdio for (&mut W, &mut W) +where + W: io::Write, +{ + fn stdio(&mut self) -> (&mut dyn io::Write, &mut dyn io::Write) { + (self.0, self.1) + } +} + /// A command execution context that captures additional data pertaining /// the command execution. pub struct ExecCtx<'io> { @@ -827,6 +868,8 @@ fn parse_arguments<'io, 'ctx: 'io>( ctx: &'ctx mut RunCtx<'_>, args: Vec<String>, ) -> Result<(Command, ExecCtx<'io>, Vec<String>)> { + use std::io::Write; + let mut model: Option<DeviceModel> = None; let model_help = format!( "Select the device model to connect to ({})", @@ -862,8 +905,16 @@ fn parse_arguments<'io, 'ctx: 'io>( "The arguments for the command", ); parser.stop_on_first_argument(true); - parse(ctx, parser, args)?; + let mut stdout_buf = BufWriter::new(ctx.stdout); + let mut stderr_buf = BufWriter::new(ctx.stderr); + let mut stdio_buf = (&mut stdout_buf, &mut stderr_buf); + let result = parse(&mut stdio_buf, parser, args); + + stdout_buf.flush()?; + stderr_buf.flush()?; + + result?; subargs.insert(0, format!("nitrocli {}", command)); let ctx = ExecCtx { |