Skip to content

Commit

Permalink
feat(snapbox): Add initial JSON support
Browse files Browse the repository at this point in the history
  • Loading branch information
Muscraft committed Aug 22, 2022
1 parent 1bfcac1 commit 412f969
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 19 deletions.
6 changes: 4 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions crates/snapbox/Cargo.toml
Expand Up @@ -49,6 +49,11 @@ path = ["tempfile", "walkdir", "dunce", "detect-encoding", "filetime"]
## Snapshotting of commands
cmd = ["os_pipe", "wait-timeout"]

## Snapshotting of json
json = ["structured-data"]
## Snapshotting of structured data
structured-data = ["serde_json"]

## Extra debugging information
debug = ["snapbox-macros/debug", "backtrace"]

Expand Down Expand Up @@ -89,3 +94,5 @@ yansi = { version = "0.5.0", optional = true }
concolor = { version = "0.0.8", optional = true }

document-features = { version = "0.2.3", optional = true }

serde_json = { version = "1.0.85", optional = true, features = ["preserve_order"]}
108 changes: 91 additions & 17 deletions crates/snapbox/src/data.rs
Expand Up @@ -10,12 +10,16 @@ pub struct Data {
enum DataInner {
Binary(Vec<u8>),
Text(String),
#[cfg(feature = "structured-data")]
Json(serde_json::Value),
}

#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash)]
pub enum DataFormat {
Binary,
Text,
#[cfg(feature = "json")]
Json,
}

impl Default for DataFormat {
Expand All @@ -39,6 +43,13 @@ impl Data {
}
}

#[cfg(feature = "json")]
pub fn json(raw: impl Into<serde_json::Value>) -> Self {
Self {
inner: DataInner::Json(raw.into()),
}
}

/// Empty test data
pub fn new() -> Self {
Self::text("")
Expand All @@ -61,11 +72,26 @@ impl Data {
.map_err(|e| format!("Failed to read {}: {}", path.display(), e))?;
Self::text(data)
}
#[cfg(feature = "json")]
DataFormat::Json => {
let data = std::fs::read_to_string(&path)
.map_err(|e| format!("Failed to read {}: {}", path.display(), e))?;
Self::json(serde_json::from_str::<serde_json::Value>(&data).unwrap())
}
},
None => {
let data = std::fs::read(&path)
.map_err(|e| format!("Failed to read {}: {}", path.display(), e))?;
Self::binary(data).try_coerce(DataFormat::Text)
let data = Self::binary(data);
match path
.extension()
.and_then(|e| e.to_str())
.unwrap_or_default()
{
#[cfg(feature = "json")]
"json" => data.try_coerce(DataFormat::Json),
_ => data.try_coerce(DataFormat::Text),
}
}
};
Ok(data)
Expand Down Expand Up @@ -96,34 +122,47 @@ impl Data {
match &self.inner {
DataInner::Binary(_) => None,
DataInner::Text(data) => Some(data.to_owned()),
#[cfg(feature = "json")]
DataInner::Json(value) => Some(serde_json::to_string_pretty(value).unwrap()),
}
}

pub fn to_bytes(&self) -> Vec<u8> {
match &self.inner {
DataInner::Binary(data) => data.clone(),
DataInner::Text(data) => data.clone().into_bytes(),
#[cfg(feature = "json")]
DataInner::Json(value) => serde_json::to_vec_pretty(value).unwrap(),
}
}

pub fn try_coerce(self, format: DataFormat) -> Self {
match format {
DataFormat::Binary => Self::binary(self.to_bytes()),
DataFormat::Text => match self.inner {
DataInner::Binary(data) => {
if is_binary(&data) {
Self::binary(data)
} else {
match String::from_utf8(data) {
Ok(data) => Self::text(data),
Err(err) => {
let data = err.into_bytes();
Self::binary(data)
}
}
}
match (self.inner, format) {
(DataInner::Binary(inner), DataFormat::Binary) => Self::binary(inner),
(DataInner::Text(inner), DataFormat::Text) => Self::text(inner),
#[cfg(feature = "json")]
(DataInner::Json(inner), DataFormat::Json) => Self::json(inner),
(DataInner::Binary(inner), _) => {
if is_binary(&inner) {
Self::binary(inner)
} else if let Ok(str) = String::from_utf8(inner.clone()) {
Self::text(str).try_coerce(format)
} else {
Self::binary(inner)
}
}
(DataInner::Text(inner), DataFormat::Binary) => Self::binary(inner.into_bytes()),
#[cfg(feature = "json")]
(DataInner::Text(inner), DataFormat::Json) => {
match serde_json::from_str::<serde_json::Value>(&inner) {
Ok(json) => Self::json(json),
Err(_) => Self::text(inner),
}
DataInner::Text(data) => Self::text(data),
}
#[cfg(feature = "json")]
(DataInner::Json(inner), _) => match serde_json::to_vec_pretty(&inner) {
Ok(bin) => Self::binary(bin).try_coerce(format),
Err(_) => Self::json(inner),
},
}
}
Expand All @@ -133,6 +172,8 @@ impl Data {
match &self.inner {
DataInner::Binary(_) => DataFormat::Binary,
DataInner::Text(_) => DataFormat::Text,
#[cfg(feature = "json")]
DataInner::Json(_) => DataFormat::Json,
}
}
}
Expand All @@ -142,6 +183,8 @@ impl std::fmt::Display for Data {
match &self.inner {
DataInner::Binary(data) => String::from_utf8_lossy(data).fmt(f),
DataInner::Text(data) => data.fmt(f),
#[cfg(feature = "json")]
DataInner::Json(data) => std::fmt::Debug::fmt(data, f),
}
}
}
Expand Down Expand Up @@ -201,6 +244,12 @@ impl Normalize for NormalizeNewlines {
let lines = crate::utils::normalize_lines(&text);
Data::text(lines)
}
#[cfg(feature = "json")]
DataInner::Json(value) => {
let mut value = value;
normalize_value(&mut value, crate::utils::normalize_lines);
Data::json(value)
}
}
}
}
Expand All @@ -214,6 +263,12 @@ impl Normalize for NormalizePaths {
let lines = crate::utils::normalize_paths(&text);
Data::text(lines)
}
#[cfg(feature = "json")]
DataInner::Json(value) => {
let mut value = value;
normalize_value(&mut value, crate::utils::normalize_paths);
Data::json(value)
}
}
}
}
Expand Down Expand Up @@ -242,7 +297,26 @@ impl Normalize for NormalizeMatches<'_> {
.normalize(&text, &self.pattern.render().unwrap());
Data::text(lines)
}
#[cfg(feature = "json")]
DataInner::Json(_) => todo!("unsure of how to do matches here"),
}
}
}

#[cfg(feature = "structured-data")]
fn normalize_value(value: &mut serde_json::Value, op: fn(&str) -> String) {
match value {
serde_json::Value::String(str) => {
*str = op(str);
}
serde_json::Value::Array(arr) => {
arr.iter_mut().for_each(|value| normalize_value(value, op));
}
serde_json::Value::Object(obj) => {
obj.iter_mut()
.for_each(|(_, value)| normalize_value(value, op));
}
_ => {}
}
}

Expand Down

0 comments on commit 412f969

Please sign in to comment.