Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(data): Add IntoData for easier initialization #292

Merged
merged 8 commits into from
Apr 22, 2024
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
Loading