Skip to content

Commit

Permalink
Merge pull request #292 from epage/into
Browse files Browse the repository at this point in the history
feat(data): Add IntoData for easier initialization
  • Loading branch information
epage authored Apr 22, 2024
2 parents 12ba903 + 8daebd3 commit 62977b5
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 88 deletions.
7 changes: 4 additions & 3 deletions crates/snapbox/src/assert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::io::stderr;

use crate::filters::{Filter as _, FilterNewlines, FilterPaths, FilterRedactions};
use crate::Action;
use crate::IntoData;

/// Snapshot assertion against a file's contents
///
Expand Down Expand Up @@ -61,9 +62,9 @@ impl Assert {
/// Assert::new().eq(file!["output.txt"], actual);
/// ```
#[track_caller]
pub fn eq(&self, expected: impl Into<crate::Data>, actual: impl Into<crate::Data>) {
let expected = expected.into();
let actual = actual.into();
pub fn eq(&self, expected: impl IntoData, actual: impl IntoData) {
let expected = expected.into_data();
let actual = actual.into_data();
if let Err(err) = self.try_eq(expected, actual, Some(&"In-memory")) {
err.panic();
}
Expand Down
18 changes: 10 additions & 8 deletions crates/snapbox/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#[cfg(feature = "color")]
use anstream::panic;

use crate::IntoData;

/// Process spawning for testing of non-interactive commands
#[derive(Debug)]
pub struct Command {
Expand Down Expand Up @@ -243,8 +245,8 @@ impl Command {
/// .assert()
/// .stdout_eq("42");
/// ```
pub fn stdin(mut self, stream: impl Into<crate::Data>) -> Self {
self.stdin = Some(stream.into());
pub fn stdin(mut self, stream: impl IntoData) -> Self {
self.stdin = Some(stream.into_data());
self
}

Expand Down Expand Up @@ -621,14 +623,14 @@ impl OutputAssert {
/// .stdout_eq(file!["stdout.log"]);
/// ```
#[track_caller]
pub fn stdout_eq(self, expected: impl Into<crate::Data>) -> Self {
let expected = expected.into();
pub fn stdout_eq(self, expected: impl IntoData) -> Self {
let expected = expected.into_data();
self.stdout_eq_inner(expected)
}

#[track_caller]
fn stdout_eq_inner(self, expected: crate::Data) -> Self {
let actual = crate::Data::from(self.output.stdout.as_slice());
let actual = self.output.stdout.as_slice().into_data();
if let Err(err) = self.config.try_eq(expected, actual, Some(&"stdout")) {
err.panic();
}
Expand Down Expand Up @@ -671,14 +673,14 @@ impl OutputAssert {
/// .stderr_eq(file!["stderr.log"]);
/// ```
#[track_caller]
pub fn stderr_eq(self, expected: impl Into<crate::Data>) -> Self {
let expected = expected.into();
pub fn stderr_eq(self, expected: impl IntoData) -> Self {
let expected = expected.into_data();
self.stderr_eq_inner(expected)
}

#[track_caller]
fn stderr_eq_inner(self, expected: crate::Data) -> Self {
let actual = crate::Data::from(self.output.stderr.as_slice());
let actual = self.output.stderr.as_slice().into_data();
if let Err(err) = self.config.try_eq(expected, actual, Some(&"stderr")) {
err.panic();
}
Expand Down
121 changes: 81 additions & 40 deletions crates/snapbox/src/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,84 @@ impl<D: std::fmt::Debug> ToDebug for D {
}
}

/// Convert to [`Data`] with modifiers for `expected` data
pub trait IntoData: Sized {
/// Remove default [`filters`][crate::filters] from this `expected` result
fn raw(self) -> Data {
self.into_data().raw()
}

/// Initialize as [`format`][DataFormat] or [`Error`][DataFormat::Error]
///
/// This is generally used for `expected` data
///
/// ```rust
/// # #[cfg(feature = "json")] {
/// use snapbox::prelude::*;
/// use snapbox::str;
///
/// let expected = str![[r#"{"hello": "world"}"#]]
/// .is(snapbox::data::DataFormat::Json);
/// assert_eq!(expected.format(), snapbox::data::DataFormat::Json);
/// # }
/// ```
fn is(self, format: DataFormat) -> Data {
self.into_data().is(format)
}

/// Convert to [`Data`], applying defaults
fn into_data(self) -> Data;
}

impl IntoData for Data {
fn into_data(self) -> Data {
self
}
}

impl IntoData for &'_ Data {
fn into_data(self) -> Data {
self.clone()
}
}

impl IntoData for Vec<u8> {
fn into_data(self) -> Data {
Data::binary(self)
}
}

impl IntoData for &'_ [u8] {
fn into_data(self) -> Data {
self.to_owned().into_data()
}
}

impl IntoData for String {
fn into_data(self) -> Data {
Data::text(self)
}
}

impl IntoData for &'_ String {
fn into_data(self) -> Data {
self.to_owned().into_data()
}
}

impl IntoData for &'_ str {
fn into_data(self) -> Data {
self.to_owned().into_data()
}
}

impl IntoData for Inline {
fn into_data(self) -> Data {
let trimmed = self.trimmed();
super::Data::text(trimmed).with_source(self)
}
}

/// Declare an expected value for an assert from a file
///
/// This is relative to the source file the macro is run from
Expand Down Expand Up @@ -80,17 +158,15 @@ macro_rules! file {

/// Declare an expected value from within Rust source
///
/// Output type: [`Inline`], see [`IntoData`] for operations
///
/// ```
/// # use snapbox::str;
/// str![["
/// Foo { value: 92 }
/// "]];
/// str![r#"{"Foo": 92}"#];
/// ```
///
/// Leading indentation is stripped.
///
/// See [`Inline::is`] for declaring the data to be of a certain format.
#[macro_export]
macro_rules! str {
[$data:literal] => { $crate::str![[$data]] };
Expand Down Expand Up @@ -140,6 +216,7 @@ pub(crate) enum DataInner {
/// - [`str!`] for inline snapshots
/// - [`file!`] for external snapshots
/// - [`ToDebug`] for verifying a debug representation
/// - [`IntoData`] for modifying `expected`
impl Data {
/// Mark the data as binary (no post-processing)
pub fn binary(raw: impl Into<Vec<u8>>) -> Self {
Expand Down Expand Up @@ -594,42 +671,6 @@ impl Default for Data {
}
}

impl<'d> From<&'d Data> for Data {
fn from(other: &'d Data) -> Self {
other.clone()
}
}

impl From<Vec<u8>> for Data {
fn from(other: Vec<u8>) -> Self {
Self::binary(other)
}
}

impl<'b> From<&'b [u8]> for Data {
fn from(other: &'b [u8]) -> Self {
other.to_owned().into()
}
}

impl From<String> for Data {
fn from(other: String) -> Self {
Self::text(other)
}
}

impl<'s> From<&'s String> for Data {
fn from(other: &'s String) -> Self {
other.clone().into()
}
}

impl<'s> From<&'s str> for Data {
fn from(other: &'s str) -> Self {
other.to_owned().into()
}
}

#[cfg(feature = "detect-encoding")]
fn is_binary(data: &[u8]) -> bool {
match content_inspector::inspect(data) {
Expand Down
2 changes: 1 addition & 1 deletion crates/snapbox/src/data/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,8 @@ impl PathRuntime {
mod tests {
use super::*;
use crate::assert_eq;
use crate::prelude::*;
use crate::str;
use crate::ToDebug as _;

#[test]
fn test_format_patch() {
Expand Down
34 changes: 1 addition & 33 deletions crates/snapbox/src/data/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,32 +79,7 @@ pub struct Inline {
}

impl Inline {
/// Initialize `Self` as [`format`][crate::data::DataFormat] or [`Error`][crate::data::DataFormat::Error]
///
/// This is generally used for `expected` data
///
/// ```rust
/// # #[cfg(feature = "json")] {
/// use snapbox::str;
///
/// let expected = str![[r#"{"hello": "world"}"#]]
/// .is(snapbox::data::DataFormat::Json);
/// assert_eq!(expected.format(), snapbox::data::DataFormat::Json);
/// # }
/// ```
pub fn is(self, format: super::DataFormat) -> super::Data {
let data: super::Data = self.into();
data.is(format)
}

/// Remove default [`filters`][crate::filters] from this `expected` result
pub fn raw(self) -> super::Data {
let mut data: super::Data = self.into();
data.filters = super::FilterSet::empty().newlines();
data
}

fn trimmed(&self) -> String {
pub(crate) fn trimmed(&self) -> String {
let mut data = self.data;
if data.contains('\n') {
data = data.strip_prefix('\n').unwrap_or(data);
Expand All @@ -120,13 +95,6 @@ impl std::fmt::Display for Inline {
}
}

impl From<Inline> for super::Data {
fn from(inline: Inline) -> Self {
let trimmed = inline.trimmed();
super::Data::text(trimmed).with_source(inline)
}
}

/// Position within Rust source code, see [`Inline`]
#[doc(hidden)]
#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down
9 changes: 8 additions & 1 deletion crates/snapbox/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,20 @@ pub use action::Action;
pub use action::DEFAULT_ACTION_ENV;
pub use assert::Assert;
pub use data::Data;
pub use data::IntoData;
pub use data::ToDebug;
pub use error::Error;
pub use filters::Redactions;
pub use snapbox_macros::debug;

pub type Result<T, E = Error> = std::result::Result<T, E>;

/// Easier access to common traits
pub mod prelude {
pub use crate::IntoData;
pub use crate::ToDebug;
}

/// Check if a value is the same as an expected value
///
/// By default [`filters`] are applied, including:
Expand All @@ -145,7 +152,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
/// assert_eq(file!["output.txt"], actual);
/// ```
#[track_caller]
pub fn assert_eq(expected: impl Into<crate::Data>, actual: impl Into<crate::Data>) {
pub fn assert_eq(expected: impl IntoData, actual: impl IntoData) {
Assert::new()
.action_env(DEFAULT_ACTION_ENV)
.eq(expected, actual);
Expand Down
5 changes: 3 additions & 2 deletions src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use rayon::prelude::*;
use snapbox::data::DataFormat;
use snapbox::filters::{Filter as _, FilterNewlines, FilterPaths, FilterRedactions};
use snapbox::path::FileType;
use snapbox::IntoData;

#[derive(Debug)]
pub(crate) struct Runner {
Expand Down Expand Up @@ -580,12 +581,12 @@ impl Output {
self.spawn.status = SpawnStatus::Ok;
self.stdout = Some(Stream {
stream: Stdio::Stdout,
content: output.stdout.into(),
content: output.stdout.into_data(),
status: StreamStatus::Ok,
});
self.stderr = Some(Stream {
stream: Stdio::Stderr,
content: output.stderr.into(),
content: output.stderr.into_data(),
status: StreamStatus::Ok,
});
self
Expand Down

0 comments on commit 62977b5

Please sign in to comment.