Skip to content

Commit

Permalink
feat(filter): Allow regexes for substitutions
Browse files Browse the repository at this point in the history
Cherry pick b5220cd (assert-rs#281)
Cherry pick 38c4fb8 (assert-rs#285)
  • Loading branch information
epage committed May 23, 2024
1 parent ea6f146 commit 3570cc3
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/snapbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ path = ["dir"]
cmd = ["dep:os_pipe", "dep:wait-timeout", "dep:libc", "dep:windows-sys"]
## Building of examples for snapshotting
examples = ["dep:escargot"]
## Regex text substitutions
regex = ["dep:regex"]

## Snapshotting of json
json = ["structured-data", "dep:serde_json", "dep:serde"]
Expand Down Expand Up @@ -92,6 +94,7 @@ document-features = { version = "0.2.6", optional = true }
serde_json = { version = "1.0.85", optional = true}
anstyle-svg = { version = "0.1.3", optional = true }
serde = { version = "1.0.198", optional = true }
regex = { version = "1.10.4", optional = true, default-features = false, features = ["std"] }

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.52.0", features = ["Win32_Foundation"], optional = true }
Expand Down
93 changes: 92 additions & 1 deletion crates/snapbox/src/filter/redactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ impl Redactions {
/// let mut subst = snapbox::Redactions::new();
/// subst.insert("[EXE]", std::env::consts::EXE_SUFFIX);
/// ```
///
/// With the `regex` feature, you can define patterns using regexes.
/// You can choose to replace a subset of the regex by giving it the named capture group
/// `redacted`.
///
/// ```rust
/// # #[cfg(feature = "regex")] {
/// let mut subst = snapbox::Redactions::new();
/// subst.insert("[OBJECT]", regex::Regex::new("(?<redacted>(world|moon))").unwrap());
/// # }
/// ```
pub fn insert(
&mut self,
placeholder: &'static str,
Expand Down Expand Up @@ -108,17 +119,34 @@ pub struct RedactedValue {
inner: Option<RedactedValueInner>,
}

#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
#[derive(Clone, Debug)]
enum RedactedValueInner {
Str(&'static str),
String(String),
#[cfg(feature = "regex")]
Regex(regex::Regex),
}

impl RedactedValueInner {
fn find_in(&self, buffer: &str) -> Option<std::ops::Range<usize>> {
match self {
Self::Str(s) => buffer.find(s).map(|offset| offset..(offset + s.len())),
Self::String(s) => buffer.find(s).map(|offset| offset..(offset + s.len())),
#[cfg(feature = "regex")]
Self::Regex(r) => {
let captures = r.captures(buffer)?;
let m = captures.name("redacted").or_else(|| captures.get(0))?;
Some(m.range())
}
}
}

fn as_cmp(&self) -> &str {
match self {
Self::Str(s) => s,
Self::String(s) => s,
#[cfg(feature = "regex")]
Self::Regex(s) => s.as_str(),
}
}
}
Expand Down Expand Up @@ -166,6 +194,42 @@ impl From<Cow<'static, str>> for RedactedValue {
}
}

#[cfg(feature = "regex")]
impl From<regex::Regex> for RedactedValue {
fn from(inner: regex::Regex) -> Self {
Self {
inner: Some(RedactedValueInner::Regex(inner)),
}
}
}

#[cfg(feature = "regex")]
impl From<&'_ regex::Regex> for RedactedValue {
fn from(inner: &'_ regex::Regex) -> Self {
inner.clone().into()
}
}

impl PartialOrd for RedactedValueInner {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.as_cmp().cmp(other.as_cmp()))
}
}

impl Ord for RedactedValueInner {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_cmp().cmp(other.as_cmp())
}
}

impl PartialEq for RedactedValueInner {
fn eq(&self, other: &Self) -> bool {
self.as_cmp().eq(other.as_cmp())
}
}

impl Eq for RedactedValueInner {}

/// Replacements is `(from, to)`
fn replace_many<'a>(
buffer: &mut String,
Expand Down Expand Up @@ -478,4 +542,31 @@ mod test {
let actual = normalize(input, pattern, &sub);
assert_eq!(actual, pattern);
}

#[test]
#[cfg(feature = "regex")]
fn substitute_regex_unnamed() {
let input = "Hello world!";
let pattern = "Hello [OBJECT]!";
let mut sub = Redactions::new();
sub.insert("[OBJECT]", regex::Regex::new("world").unwrap())
.unwrap();
let actual = normalize(input, pattern, &sub);
assert_eq!(actual, pattern);
}

#[test]
#[cfg(feature = "regex")]
fn substitute_regex_named() {
let input = "Hello world!";
let pattern = "Hello [OBJECT]!";
let mut sub = Redactions::new();
sub.insert(
"[OBJECT]",
regex::Regex::new("(?<redacted>world)!").unwrap(),
)
.unwrap();
let actual = normalize(input, pattern, &sub);
assert_eq!(actual, pattern);
}
}

0 comments on commit 3570cc3

Please sign in to comment.