Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 62 additions & 24 deletions src/container.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::Codec;
use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use std::fmt;

#[derive(Debug, Clone, Default)]
pub enum BytesContainerType {
Expand All @@ -23,9 +24,6 @@ pub struct BytesContainer {
/// The JSON represantation of the bytes.
#[serde(flatten)]
pub json_value: Option<serde_json::Value>,
/// The human readable representation of the bytes.
#[serde(skip)]
pub humanized: String,
/// Context for encoding errors.
#[serde(skip)]
pub encoding_error: Option<String>,
Expand All @@ -35,7 +33,6 @@ impl Default for BytesContainer {
fn default() -> Self {
Self {
codec: Codec::Raw,
humanized: "<raw codec>".into(),
json_value: None,
raw: Default::default(),
encoding_error: None,
Expand All @@ -46,20 +43,11 @@ impl Default for BytesContainer {
impl BytesContainer {
pub fn new(ty: BytesContainerType, codec: Codec, raw: Vec<u8>) -> Result<Self> {
match codec {
Codec::Raw => {
let humanized = raw
.iter()
.map(|b| format!("{:02x}", b))
.collect::<Vec<String>>()
.join(" ");

Ok(Self {
raw,
codec,
humanized,
..Default::default()
})
}
Codec::Raw => Ok(Self {
raw,
codec,
..Default::default()
}),
Codec::Json => match ty {
BytesContainerType::Input => {
let json = serde_json::from_slice::<serde_json::Value>(&raw)
Expand All @@ -71,24 +59,22 @@ impl BytesContainer {
codec,
raw: minified_buffer,
json_value: Some(json.clone()),
humanized: serde_json::to_string_pretty(&json)?,
encoding_error: None,
})
}
BytesContainerType::Output => {
let mut this = Self {
raw: raw.clone(),
codec,
..Default::default()
};

match serde_json::from_slice::<serde_json::Value>(&raw) {
Ok(json) => {
this.json_value = Some(json.clone());
this.humanized = serde_json::to_string_pretty(&json)?;
this.raw = serde_json::to_vec(&json)?;
}
Err(e) => {
this.humanized = String::from_utf8_lossy(&raw).into();
this.encoding_error = Some(e.to_string());
}
};
Expand All @@ -107,12 +93,12 @@ impl BytesContainer {
raw: bytes,
codec,
json_value: Some(json.clone()),
humanized: serde_json::to_string_pretty(&json)?,
encoding_error: None,
})
}
BytesContainerType::Output => {
let mut this = Self {
raw: raw.clone(),
codec,
..Default::default()
};
Expand All @@ -121,11 +107,9 @@ impl BytesContainer {
match value {
Ok(json) => {
this.json_value = Some(json.clone());
this.humanized = serde_json::to_string_pretty(&json)?;
this.raw = raw;
}
Err(e) => {
this.humanized = String::from_utf8_lossy(&raw).into();
this.encoding_error = Some(e.to_string());
}
};
Expand All @@ -137,6 +121,45 @@ impl BytesContainer {
}
}

pub struct HumanizedBytes<'a> {
bytes: &'a BytesContainer,
}

impl<'a> From<&'a BytesContainer> for HumanizedBytes<'a> {
fn from(bytes: &'a BytesContainer) -> Self {
Self { bytes }
}
}

impl fmt::Display for HumanizedBytes<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(json) = &self.bytes.json_value {
let humanized = serde_json::to_string_pretty(json).map_err(|_| fmt::Error)?;
return formatter.write_str(&humanized);
}

if self.bytes.encoding_error.is_some() {
return formatter.write_str(&String::from_utf8_lossy(&self.bytes.raw));
}

match self.bytes.codec {
Codec::Raw => {
for (index, byte) in self.bytes.raw.iter().enumerate() {
if index > 0 {
formatter.write_str(" ")?;
}
write!(formatter, "{byte:02x}")?;
}

Ok(())
}
Codec::Json | Codec::Messagepack => {
formatter.write_str(&String::from_utf8_lossy(&self.bytes.raw))
}
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -153,4 +176,19 @@ mod tests {
String::from_utf8(raw).unwrap()
);
}

#[test]
fn humanized_bytes_can_be_composed_from_a_bytes_container() {
let input = BytesContainer::new(
BytesContainerType::Input,
Codec::Json,
br#"{"input_test":"input_value"}"#.to_vec(),
)
.expect("valid JSON input");

assert_eq!(
HumanizedBytes::from(&input).to_string(),
"{\n \"input_test\": \"input_value\"\n}"
);
}
}
17 changes: 10 additions & 7 deletions src/function_run_result.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::BytesContainer;
use crate::{BytesContainer, HumanizedBytes};
use colored::Colorize;
use serde::{Deserialize, Serialize};
use std::fmt;
Expand Down Expand Up @@ -85,7 +85,7 @@ impl fmt::Display for FunctionRunResult {
formatter,
"{}\n\n{}",
" Input ".black().on_bright_yellow(),
self.input.humanized,
HumanizedBytes::from(&self.input),
)?;

writeln!(
Expand All @@ -111,7 +111,7 @@ impl fmt::Display for FunctionRunResult {
formatter,
"{}\n\n{}",
" Invalid Output ".black().on_bright_red(),
self.output.humanized,
HumanizedBytes::from(&self.output),
)?;

writeln!(
Expand All @@ -125,7 +125,7 @@ impl fmt::Display for FunctionRunResult {
formatter,
"{}\n\n{}",
" Output ".black().on_bright_green(),
self.output.humanized,
HumanizedBytes::from(&self.output),
)?;
}

Expand Down Expand Up @@ -248,9 +248,10 @@ mod tests {
success: true,
};

let input_humanized = HumanizedBytes::from(&input).to_string();
let predicate = predicates::str::contains("Instructions: 1.001K")
.and(predicates::str::contains("Linear Memory Usage: 1000KB"))
.and(predicates::str::contains(input.humanized))
.and(predicates::str::contains(input_humanized))
.and(predicates::str::contains("Input Size: 28B"))
.and(predicates::str::contains("Output Size: 15B"));
assert!(predicate.eval(&function_run_result.to_string()));
Expand Down Expand Up @@ -278,9 +279,10 @@ mod tests {
success: true,
};

let input_humanized = HumanizedBytes::from(&input).to_string();
let predicate = predicates::str::contains("Instructions: 1")
.and(predicates::str::contains("Linear Memory Usage: 1000KB"))
.and(predicates::str::contains(input.humanized));
.and(predicates::str::contains(input_humanized));
assert!(predicate.eval(&function_run_result.to_string()));
Ok(())
}
Expand All @@ -304,9 +306,10 @@ mod tests {
success: true,
};

let input_humanized = HumanizedBytes::from(&input).to_string();
let predicate = predicates::str::contains("Instructions: 999")
.and(predicates::str::contains("Linear Memory Usage: 1000KB"))
.and(predicates::str::contains(input.humanized));
.and(predicates::str::contains(input_humanized));
assert!(predicate.eval(&function_run_result.to_string()));
Ok(())
}
Expand Down