From ff3c5fd8f6370265156d308b44c3b039ead12fd4 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 17 Feb 2019 13:10:36 +0000 Subject: 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. --- nitrocli/src/args.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) 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 = result::Result; +/// 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, + 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 { + 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 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, ) -> Result<(Command, ExecCtx<'io>, Vec)> { + use std::io::Write; + let mut model: Option = 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 { -- cgit v1.2.1