From e46c53d9cf12ed66a15916f9fefcc4b682b21d57 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 22 Apr 2024 15:42:02 -0500 Subject: [PATCH] docs(data): Clarify how to use the API Cherry pick 62ddc26979e9839d247bf25b9dac83ad1dfd181b (#290) --- crates/snapbox/src/data/format.rs | 6 +++ crates/snapbox/src/data/mod.rs | 73 ++++++++++++++++++++-------- crates/snapbox/src/data/normalize.rs | 6 +++ crates/snapbox/src/data/source.rs | 14 +++++- 4 files changed, 77 insertions(+), 22 deletions(-) diff --git a/crates/snapbox/src/data/format.rs b/crates/snapbox/src/data/format.rs index cc573b52..8af0ee6e 100644 --- a/crates/snapbox/src/data/format.rs +++ b/crates/snapbox/src/data/format.rs @@ -1,16 +1,22 @@ +/// Describes the structure of [`Data`][crate::Data] #[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Default)] pub enum DataFormat { + /// Processing of the [`Data`][crate::Data] failed Error, + /// Non-textual, opaque data Binary, #[default] Text, #[cfg(feature = "json")] Json, + /// [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#DOS_and_Windows) + /// rendered as [svg](https://docs.rs/anstyle-svg) #[cfg(feature = "term-svg")] TermSvg, } impl DataFormat { + /// Assumed file extension for the format pub fn ext(self) -> &'static str { match self { Self::Error => "txt", diff --git a/crates/snapbox/src/data/mod.rs b/crates/snapbox/src/data/mod.rs index 389d4f2f..1d32dc39 100644 --- a/crates/snapbox/src/data/mod.rs +++ b/crates/snapbox/src/data/mod.rs @@ -1,3 +1,5 @@ +//! `actual` and `expected` [`Data`] for testing code + mod format; mod normalize; mod runtime; @@ -12,8 +14,26 @@ pub use normalize::NormalizeNewlines; pub use normalize::NormalizePaths; pub use source::DataSource; pub use source::Inline; +#[doc(hidden)] pub use source::Position; +/// Capture the pretty debug representation of a value +/// +/// Note: this is fairly brittle as debug representations are not generally subject to semver +/// guarantees. +/// +/// ```rust,no_run +/// use snapbox::ToDebug as _; +/// +/// fn some_function() -> usize { +/// // ... +/// # 5 +/// } +/// +/// let actual = some_function(); +/// let expected = snapbox::str![["5"]]; +/// snapbox::assert_eq(actual.to_debug(), expected); +/// ``` pub trait ToDebug { fn to_debug(&self) -> Data; } @@ -113,14 +133,13 @@ pub(crate) enum DataInner { TermSvg(String), } +/// # Constructors +/// +/// See also +/// - [`str!`] for inline snapshots +/// - [`file!`] for external snapshots +/// - [`ToDebug`] for verifying a debug representation impl Data { - pub(crate) fn with_inner(inner: DataInner) -> Self { - Self { - inner, - source: None, - } - } - /// Mark the data as binary (no post-processing) pub fn binary(raw: impl Into>) -> Self { Self::with_inner(DataInner::Binary(raw.into())) @@ -148,15 +167,6 @@ impl Data { Self::text("") } - fn with_source(mut self, source: impl Into) -> Self { - self.source = Some(source.into()); - self - } - - fn with_path(self, path: impl Into) -> Self { - self.with_source(path.into()) - } - /// Load `expected` data from a file pub fn read_from(path: &std::path::Path, data_format: Option) -> Self { match Self::try_read_from(path, data_format) { @@ -165,6 +175,27 @@ impl Data { .with_path(path), } } +} + +/// # Assertion frameworks operations +/// +/// For example, see [`OutputAssert`][crate::cmd::OutputAssert] +impl Data { + pub(crate) fn with_inner(inner: DataInner) -> Self { + Self { + inner, + source: None, + } + } + + fn with_source(mut self, source: impl Into) -> Self { + self.source = Some(source.into()); + self + } + + fn with_path(self, path: impl Into) -> Self { + self.with_source(path.into()) + } /// Load `expected` data from a file pub fn try_read_from( @@ -193,11 +224,6 @@ impl Data { Ok(data.with_path(path)) } - /// Location the data came from - pub fn source(&self) -> Option<&DataSource> { - self.source.as_ref() - } - /// Overwrite a snapshot pub fn write_to(&self, source: &DataSource) -> Result<(), crate::Error> { match &source.inner { @@ -382,6 +408,11 @@ impl Data { Self { inner, source } } + /// Location the data came from + pub fn source(&self) -> Option<&DataSource> { + self.source.as_ref() + } + /// Outputs the current `DataFormat` of the underlying data pub fn format(&self) -> DataFormat { match &self.inner { diff --git a/crates/snapbox/src/data/normalize.rs b/crates/snapbox/src/data/normalize.rs index 230fd804..fc8339c7 100644 --- a/crates/snapbox/src/data/normalize.rs +++ b/crates/snapbox/src/data/normalize.rs @@ -1,3 +1,9 @@ +//! Normalize `actual` or `expected` [`Data`] +//! +//! This can be done for +//! - Making snapshots consistent across platforms or conditional compilation +//! - Focusing snapshots on the characteristics of the data being tested + use super::Data; use super::DataInner; diff --git a/crates/snapbox/src/data/source.rs b/crates/snapbox/src/data/source.rs index 5dd3aaa1..cf8162c2 100644 --- a/crates/snapbox/src/data/source.rs +++ b/crates/snapbox/src/data/source.rs @@ -1,3 +1,4 @@ +/// Origin of a snapshot so it can be updated #[derive(Clone, Debug, PartialEq, Eq)] pub struct DataSource { pub(crate) inner: DataSourceInner, @@ -68,7 +69,7 @@ impl std::fmt::Display for DataSource { } } -/// Data from within Rust source code +/// Output of [`str!`][crate::str!] #[derive(Clone, Debug, PartialEq, Eq)] pub struct Inline { #[doc(hidden)] @@ -89,6 +90,16 @@ 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) @@ -148,6 +159,7 @@ impl From for super::Data { } /// Position within Rust source code, see [`Inline`] +#[doc(hidden)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct Position { #[doc(hidden)]