Skip to content

Commit

Permalink
Added compact JSON format (#288)
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed Sep 26, 2022
1 parent e444bb7 commit 7d74731
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 35 deletions.
26 changes: 13 additions & 13 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,16 @@ jobs:
- name: Test
run: make test

build-old-stable:
name: Check on 1.51.0
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.51.0
profile: minimal
override: true
- name: Check
run: cargo check --no-default-features
## our dependencies no longer support this
# build-old-stable:
# name: Check on 1.51.0
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v1
# - uses: actions-rs/toolchain@v1
# with:
# toolchain: 1.51.0
# profile: minimal
# override: true
# - name: Check
# run: cargo check --no-default-features
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ license = "Apache-2.0"
authors = ["Armin Ronacher <armin.ronacher@active-4.com>"]
description = "A snapshot testing library for Rust"
edition = "2018"
rust-version = "1.51.0"
rust-version = "1.56.0"
homepage = "https://insta.rs/"
repository = "https://github.com/mitsuhiko/insta"
keywords = ["snapshot", "testing", "jest", "approval"]
Expand Down
70 changes: 53 additions & 17 deletions src/content/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use std::fmt::{Display, Write};

use crate::content::Content;

/// The maximum number of characters to print in a single line
/// when [`to_string_pretty`] is used.
const COMPACT_MAX_CHARS: usize = 120;

pub fn format_float<T: Display>(value: T) -> String {
let mut rv = format!("{}", value);
if !rv.contains('.') {
Expand All @@ -10,10 +14,17 @@ pub fn format_float<T: Display>(value: T) -> String {
rv
}

#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Format {
Condensed,
SingleLine,
Pretty,
}

/// Serializes a serializable to JSON.
pub struct Serializer {
out: String,
pretty: bool,
format: Format,
indentation: usize,
}

Expand All @@ -22,7 +33,7 @@ impl Serializer {
pub fn new() -> Serializer {
Serializer {
out: String::new(),
pretty: false,
format: Format::Condensed,
indentation: 0,
}
}
Expand All @@ -32,7 +43,7 @@ impl Serializer {
}

fn write_indentation(&mut self) {
if self.pretty {
if self.format == Format::Pretty {
write!(self.out, "{: ^1$}", "", self.indentation * 2).unwrap();
}
}
Expand All @@ -44,31 +55,40 @@ impl Serializer {

fn end_container(&mut self, c: char, empty: bool) {
self.indentation -= 1;
if self.pretty && !empty {
if self.format == Format::Pretty && !empty {
self.write_char('\n');
self.write_indentation();
}
self.write_char(c);
}

fn write_comma(&mut self, first: bool) {
if self.pretty {
if first {
self.write_char('\n');
} else {
self.write_str(",\n");
match self.format {
Format::Pretty => {
if first {
self.write_char('\n');
} else {
self.write_str(",\n");
}
self.write_indentation();
}
Format::Condensed => {
if !first {
self.write_char(',');
}
}
Format::SingleLine => {
if !first {
self.write_str(", ");
}
}
self.write_indentation();
} else if !first {
self.write_char(',');
}
}

fn write_colon(&mut self) {
if self.pretty {
self.write_str(": ");
} else {
self.write_char(':');
match self.format {
Format::Pretty | Format::SingleLine => self.write_str(": "),
Format::Condensed => self.write_char(':'),
}
}

Expand Down Expand Up @@ -277,11 +297,27 @@ pub fn to_string(value: &Content) -> String {
ser.into_result()
}

/// Serializes a value to JSON in single-line format.
pub fn to_string_compact(value: &Content) -> String {
let mut ser = Serializer::new();
ser.format = Format::SingleLine;
ser.serialize(value);
let rv = ser.into_result();
// this is pretty wasteful as we just format twice
// but it's acceptable for the way this is used in
// insta.
if rv.chars().count() > COMPACT_MAX_CHARS {
to_string_pretty(value)
} else {
rv
}
}

/// Serializes a value to JSON pretty
#[allow(unused)]
pub fn to_string_pretty(value: &Content) -> String {
let mut ser = Serializer::new();
ser.pretty = true;
ser.format = Format::Pretty;
ser.serialize(value);
ser.into_result()
}
Expand Down
49 changes: 47 additions & 2 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ macro_rules! assert_toml_snapshot {

/// Asserts a `Serialize` snapshot in YAML format.
///
/// **Feature:** `yaml` (to be disabled by default)
/// **Feature:** `yaml`
///
/// The value needs to implement the `serde::Serialize` trait and the snapshot
/// will be serialized in YAML format. This does mean that unlike the debug
Expand Down Expand Up @@ -216,7 +216,7 @@ macro_rules! assert_ron_snapshot {

/// Asserts a `Serialize` snapshot in JSON format.
///
/// **Feature:** `json` (to be disabled by default)
/// **Feature:** `json`
///
/// This works exactly like [`assert_yaml_snapshot!`] but serializes in JSON format.
/// This is normally not recommended because it makes diffs less reliable, but it can
Expand Down Expand Up @@ -258,6 +258,51 @@ macro_rules! assert_json_snapshot {
}};
}

/// Asserts a `Serialize` snapshot in compact JSON format.
///
/// **Feature:** `json`
///
/// This works exactly like [`assert_json_snapshot!`] but serializes into a single
/// line for as long as the output is less than 120 characters. This can be useful
/// in cases where you are working with small result outputs but comes at the cost
/// of slightly worse diffing behavior.
///
/// Example:
///
/// ```no_run
/// # use insta::*;
/// assert_compact_json_snapshot!(vec![1, 2, 3]);
/// ```
///
/// The third argument to the macro can be an object expression for redaction.
/// It's in the form `{ selector => replacement }`. For more information
/// about redactions refer to the [redactions feature in the guide](https://insta.rs/docs/redactions/).
///
/// The snapshot name is optional but can be provided as first argument.
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
#[macro_export]
macro_rules! assert_compact_json_snapshot {
($value:expr, @$snapshot:literal) => {{
$crate::_assert_serialized_snapshot!($value, JsonCompact, @$snapshot);
}};
($value:expr, {$($k:expr => $v:expr),*$(,)?}, @$snapshot:literal) => {{
$crate::_assert_serialized_snapshot!($value, {$($k => $v),*}, JsonCompact, @$snapshot);
}};
($value:expr, {$($k:expr => $v:expr),*$(,)?}) => {{
$crate::_assert_serialized_snapshot!($crate::_macro_support::AutoName, $value, {$($k => $v),*}, JsonCompact);
}};
($name:expr, $value:expr) => {{
$crate::_assert_serialized_snapshot!(Some($name), $value, JsonCompact);
}};
($name:expr, $value:expr, {$($k:expr => $v:expr),*$(,)?}) => {{
$crate::_assert_serialized_snapshot!(Some($name), $value, {$($k => $v),*}, JsonCompact);
}};
($value:expr) => {{
$crate::_assert_serialized_snapshot!($crate::_macro_support::AutoName, $value, JsonCompact);
}};
}

#[doc(hidden)]
#[macro_export]
macro_rules! _assert_serialized_snapshot {
Expand Down
2 changes: 2 additions & 0 deletions src/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub enum SerializationFormat {
Toml,
Yaml,
Json,
JsonCompact,
}

pub enum SnapshotLocation {
Expand Down Expand Up @@ -47,6 +48,7 @@ pub fn serialize_content(
}
}
SerializationFormat::Json => json::to_string_pretty(&content),
SerializationFormat::JsonCompact => json::to_string_compact(&content),
#[cfg(feature = "csv")]
SerializationFormat::Csv => {
let mut buf = Vec::with_capacity(128);
Expand Down
47 changes: 45 additions & 2 deletions tests/test_inline.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#[cfg(feature = "csv")]
use insta::assert_csv_snapshot;
#[cfg(feature = "json")]
use insta::assert_json_snapshot;
#[cfg(feature = "ron")]
use insta::assert_ron_snapshot;
#[cfg(feature = "toml")]
use insta::assert_toml_snapshot;
#[cfg(feature = "yaml")]
use insta::assert_yaml_snapshot;
#[cfg(feature = "json")]
use insta::{assert_compact_json_snapshot, assert_json_snapshot};

use insta::{assert_debug_snapshot, assert_snapshot};
use std::thread;
Expand Down Expand Up @@ -235,3 +235,46 @@ fn test_multiline_with_empty_lines() {
# alternative
"###);
}

#[cfg(feature = "json")]
#[test]
fn test_compact_json() {
assert_compact_json_snapshot!((1..30).collect::<Vec<_>>(), @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]");
assert_compact_json_snapshot!((1..34).collect::<Vec<_>>(), @r###"
[
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33
]
"###);
}

0 comments on commit 7d74731

Please sign in to comment.