From 9d72c22a43783ef3576933fd3d35253fc37ab030 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 23 Jul 2022 11:02:06 +0200 Subject: [PATCH] Added new filters feature. --- CHANGELOG.md | 1 + Cargo.toml | 4 ++++ src/lib.rs | 6 +++++ src/runtime.rs | 5 +++++ src/settings.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++- src/snapshot.rs | 10 +++++++++ 6 files changed, 83 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5def393..39dc586c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to insta and cargo-insta are documented here. - Fixed an issue in `cargo-insta` where sometimes accepting inline snapshots would crash with an out of bounds panic. +- Added new `filters` feature. ## 1.16.0 diff --git a/Cargo.toml b/Cargo.toml index ab657b70..ce8a886c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,9 @@ default = ["colors"] # snapshots. redactions = ["pest", "pest_derive"] +# Enables support for running filters on snapshot +filters = ["regex"] + # Glob support glob = ["walkdir", "globset"] @@ -50,6 +53,7 @@ globset = { version = "0.4.6", optional = true } walkdir = { version = "2.3.1", optional = true } similar = { version = "2.1.0", features = ["inline"] } once_cell = "1.9.0" +regex = { version = "1.6.0", default-features = false, optional = true, features = ["std", "unicode"] } [dev-dependencies] similar-asserts = "1.2.0" diff --git a/src/lib.rs b/src/lib.rs index 49ccb4c4..d5bf7757 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,6 +140,7 @@ //! * `ron`: enables RON support ([`assert_ron_snapshot!`]) //! * `toml`: enables TOML support ([`assert_toml_snapshot!`]) //! * `redactions`: enables support for redactions +//! * `filters`: enables support for filters //! * `glob`: enables support for globbing ([`glob!`]) //! * `colors`: enables color output (enabled by default) //! @@ -161,6 +162,9 @@ mod utils; #[cfg(feature = "redactions")] mod redaction; +#[cfg(feature = "filters")] +mod filters; + #[cfg(feature = "glob")] mod glob; @@ -176,6 +180,8 @@ pub use crate::snapshot::{MetaData, Snapshot}; /// are exposed for documentation primarily. pub mod internals { pub use crate::content::Content; + #[cfg(feature = "filters")] + pub use crate::filters::Filters; pub use crate::runtime::AutoName; pub use crate::snapshot::{MetaData, SnapshotContents}; #[cfg(feature = "redactions")] diff --git a/src/runtime.rs b/src/runtime.rs index 665935f1..68b4d5d2 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -472,6 +472,11 @@ pub fn assert_snapshot( assertion_line, )?; + // apply filters if they are available + #[cfg(feature = "filters")] + let new_snapshot_value = + Settings::with(|settings| settings.filters().apply_to(new_snapshot_value)); + let new_snapshot = ctx.new_snapshot(new_snapshot_value.into(), expr); // memoize the snapshot file if requested. diff --git a/src/settings.rs b/src/settings.rs index 3d531b47..7a9a52b2 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -14,6 +14,9 @@ use crate::{ redaction::{dynamic_redaction, sorted_redaction, ContentPath, Redaction, Selector}, }; +#[cfg(feature = "filters")] +use crate::filters::Filters; + static DEFAULT_SETTINGS: Lazy> = Lazy::new(|| { Arc::new(ActualSettings { sort_maps: false, @@ -26,6 +29,8 @@ static DEFAULT_SETTINGS: Lazy> = Lazy::new(|| { prepend_module_to_snapshot: true, #[cfg(feature = "redactions")] redactions: Redactions::default(), + #[cfg(feature = "filters")] + filters: Filters::default(), #[cfg(feature = "glob")] allow_empty_glob: false, }) @@ -62,6 +67,8 @@ pub struct ActualSettings { pub prepend_module_to_snapshot: bool, #[cfg(feature = "redactions")] pub redactions: Redactions, + #[cfg(feature = "filters")] + pub filters: Filters, #[cfg(feature = "glob")] pub allow_empty_glob: bool, } @@ -104,6 +111,11 @@ impl ActualSettings { self.redactions = r.into(); } + #[cfg(feature = "filters")] + pub fn filters>(&mut self, f: F) { + self.filters = f.into(); + } + #[cfg(feature = "glob")] pub fn allow_empty_glob(&mut self, value: bool) { self.allow_empty_glob = value; @@ -370,7 +382,7 @@ impl Settings { /// The default set is empty. #[cfg(feature = "redactions")] pub fn set_redactions>(&mut self, redactions: R) { - self._private_inner_mut().redactions = redactions.into(); + self._private_inner_mut().redactions(redactions); } /// Removes all redactions. @@ -389,6 +401,50 @@ impl Settings { .map(|&(ref a, ref b)| (a, &**b)) } + /// Adds a new filter. + /// + /// Filters are similar to redactions but are applied as regex onto the final snapshot + /// value. This can be used to perform modifications to the snapshot string that would + /// be impossible to do with redactions because for instance the value is just a string. + /// + /// The first argument is the [`regex`] pattern to apply, the second is a replacement + /// string. The replacement string has the same functionality as the second argument + /// to [`Regex::replace`](regex::Regex::replace). + /// + /// This is useful to perform some cleanup procedures on the snapshot for unstable values. + /// + /// ```rust + /// # use insta::Settings; + /// # async fn foo() { + /// # let mut settings = Settings::new(); + /// settings.add_filter(r"\b[[:xdigit:]]{32}\b", "[UID]"); + /// # } + /// ``` + #[cfg(feature = "filters")] + pub fn add_filter>(&mut self, regex: &str, replacement: S) { + self._private_inner_mut().filters.add(regex, replacement); + } + + /// Replaces the currently set filters. + /// + /// The default set is empty. + #[cfg(feature = "filters")] + pub fn set_filters>(&mut self, filters: F) { + self._private_inner_mut().filters(filters); + } + + /// Removes all filters. + #[cfg(feature = "filters")] + pub fn clear_filters(&mut self) { + self._private_inner_mut().filters.clear(); + } + + /// Returns the current filters + #[cfg(feature = "filters")] + pub(crate) fn filters(&self) -> &Filters { + &self.inner.filters + } + /// Sets the snapshot path. /// /// If not absolute it's relative to where the test is in. diff --git a/src/snapshot.rs b/src/snapshot.rs index 3874b2df..454ae0e0 100644 --- a/src/snapshot.rs +++ b/src/snapshot.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::error::Error; use std::fs; use std::io::{BufRead, BufReader, Write}; @@ -349,6 +350,15 @@ impl SnapshotContents { } } +impl<'a> From> for SnapshotContents { + fn from(value: Cow<'a, str>) -> Self { + match value { + Cow::Borrowed(s) => SnapshotContents::from(s), + Cow::Owned(s) => SnapshotContents::from(s), + } + } +} + impl From<&str> for SnapshotContents { fn from(value: &str) -> SnapshotContents { // make sure we have unix newlines consistently