diff --git a/src/cli/decode.rs b/src/cli/decode.rs index 8eabf0ff..9085544d 100644 --- a/src/cli/decode.rs +++ b/src/cli/decode.rs @@ -9,7 +9,7 @@ use std::{ use clap::{Args, ValueEnum}; use serde::Serialize; -use crate::cli::Channel; +use crate::cli::{skip_whitespace::SkipWhitespace, Channel}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -82,27 +82,33 @@ macro_rules! run_x { Error::UnknownType(self.r#type.clone(), &crate::$m::TypeVariant::VARIANTS_STR) })?; for f in &mut files { - let mut f = crate::$m::Limited::new(f, crate::$m::Limits::none()); match self.input { InputFormat::Single => { + let mut f = crate::$m::Limited::new(f, crate::$m::Limits::none()); let t = crate::$m::Type::read_xdr_to_end(r#type, &mut f)?; self.out(&t)?; } InputFormat::SingleBase64 => { + let f = SkipWhitespace::new(f); + let mut f = crate::$m::Limited::new(f, crate::$m::Limits::none()); let t = crate::$m::Type::read_xdr_base64_to_end(r#type, &mut f)?; self.out(&t)?; } InputFormat::Stream => { + let mut f = crate::$m::Limited::new(f, crate::$m::Limits::none()); for t in crate::$m::Type::read_xdr_iter(r#type, &mut f) { self.out(&t?)?; } } InputFormat::StreamBase64 => { + let f = SkipWhitespace::new(f); + let mut f = crate::$m::Limited::new(f, crate::$m::Limits::none()); for t in crate::$m::Type::read_xdr_base64_iter(r#type, &mut f) { self.out(&t?)?; } } InputFormat::StreamFramed => { + let mut f = crate::$m::Limited::new(f, crate::$m::Limits::none()); for t in crate::$m::Type::read_xdr_framed_iter(r#type, &mut f) { self.out(&t?)?; } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 68dd0fb6..6128be04 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,6 +1,7 @@ pub mod decode; pub mod encode; pub mod guess; +mod skip_whitespace; pub mod types; mod version; diff --git a/src/cli/skip_whitespace.rs b/src/cli/skip_whitespace.rs new file mode 100644 index 00000000..dad214e7 --- /dev/null +++ b/src/cli/skip_whitespace.rs @@ -0,0 +1,70 @@ +use std::io::Read; + +/// Forwards read operations to the wrapped object, skipping over any whitespace +/// in what is written to buf when the function returns. +pub struct SkipWhitespace { + inner: R, +} + +impl SkipWhitespace { + pub fn new(inner: R) -> Self { + SkipWhitespace { inner } + } +} + +impl Read for SkipWhitespace { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let n = self.inner.read(buf)?; + + let mut written = 0; + for read in 0..n { + if !buf[read].is_ascii_whitespace() { + buf[written] = buf[read]; + written += 1; + } + } + + Ok(written) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test() { + struct Test { + input: &'static [u8], + output: &'static [u8], + } + let tests = [ + Test { + input: b"", + output: b"", + }, + Test { + input: b" \n\t\r", + output: b"", + }, + Test { + input: b"a c", + output: b"ac", + }, + Test { + input: b"ab cd", + output: b"abcd", + }, + Test { + input: b" ab \n cd ", + output: b"abcd", + }, + ]; + for (i, t) in tests.iter().enumerate() { + let mut skip = SkipWhitespace::new(t.input); + let mut output = Vec::new(); + skip.read_to_end(&mut output).unwrap(); + assert_eq!(output, t.output, "#{i}"); + } + } +}