Skip to content

Commit d4b4cdd

Browse files
committed
Add filter to perform replacements via regular expressions
Signed-off-by: Ryan Bottriell <[email protected]>
1 parent c2dac9a commit d4b4cdd

File tree

5 files changed

+112
-0
lines changed

5 files changed

+112
-0
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/spk-schema/crates/liquid/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ migration-to-components = ["spk-schema-foundation/migration-to-components"]
1010
[dependencies]
1111
liquid = "0.26.0"
1212
liquid-core = "0.26.0"
13+
regex = "1.6.0"
1314
serde = "1.0"
1415
serde_json = "1.0"
1516
spk-schema-foundation = { path = "../foundation" }
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) Sony Pictures Imageworks, et al.
2+
// SPDX-License-Identifier: Apache-2.0
3+
// https://github.com/imageworks/spk
4+
5+
use liquid::ValueView;
6+
use liquid_core::{
7+
Display_filter,
8+
Expression,
9+
Filter,
10+
FilterParameters,
11+
FilterReflection,
12+
FromFilterParameters,
13+
ParseFilter,
14+
Result,
15+
Runtime,
16+
Value,
17+
};
18+
19+
#[cfg(test)]
20+
#[path = "./filter_replace_regex_test.rs"]
21+
mod filter_replace_regex_test;
22+
23+
#[derive(Debug, FilterParameters)]
24+
struct ReplaceRegexArgs {
25+
#[parameter(description = "The regular expression to search.", arg_type = "str")]
26+
search: Expression,
27+
#[parameter(
28+
description = "The text to replace search results with. If not given, the filter will just delete search results. Capture groups can be substituted using `$<name_or_number>`",
29+
arg_type = "str"
30+
)]
31+
replace: Option<Expression>,
32+
}
33+
34+
#[derive(Clone, ParseFilter, FilterReflection)]
35+
#[filter(
36+
name = "replace_re",
37+
description = "Like `replace`, but searches using a regular expression.",
38+
parameters(ReplaceRegexArgs),
39+
parsed(ReplaceRegexFilter)
40+
)]
41+
pub struct ReplaceRegex;
42+
43+
#[derive(Debug, FromFilterParameters, Display_filter)]
44+
#[name = "replace_re"]
45+
struct ReplaceRegexFilter {
46+
#[parameters]
47+
args: ReplaceRegexArgs,
48+
}
49+
50+
impl Filter for ReplaceRegexFilter {
51+
fn evaluate(&self, input: &dyn ValueView, runtime: &dyn Runtime) -> Result<Value> {
52+
let args = self.args.evaluate(runtime)?;
53+
54+
let input = input.to_kstr();
55+
56+
let search = regex::Regex::new(&args.search)
57+
.map_err(|err| liquid::Error::with_msg(err.to_string()))?;
58+
let replace = args.replace.unwrap_or_else(|| "".into());
59+
60+
Ok(Value::scalar(
61+
search.replace_all(&input, replace.as_str()).to_string(),
62+
))
63+
}
64+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) Sony Pictures Imageworks, et al.
2+
// SPDX-License-Identifier: Apache-2.0
3+
// https://github.com/imageworks/spk
4+
5+
use rstest::rstest;
6+
use serde_json::json;
7+
8+
#[rstest]
9+
fn test_replace_regex_basic() {
10+
let options = json!({});
11+
static TPL: &str = r#"{{ "1992-02-25" | replace_re: "(\d+)-(\d+)-(\d+)", "$3/$2/$1" }}"#;
12+
static EXPECTED: &str = r#"25/02/1992"#;
13+
let rendered =
14+
crate::render_template(TPL, &options).expect("template should not fail to render");
15+
assert_eq!(rendered, EXPECTED);
16+
}
17+
18+
#[rstest]
19+
fn test_replace_regex_empty() {
20+
let options = json!({});
21+
static TPL: &str = r#"{{ "Hello, World!" | replace_re: "[A-Z]" }}"#;
22+
static EXPECTED: &str = r#"ello, orld!"#;
23+
let rendered =
24+
crate::render_template(TPL, &options).expect("template should not fail to render");
25+
assert_eq!(rendered, EXPECTED);
26+
}
27+
28+
#[rstest]
29+
fn test_replace_regex_compile_error() {
30+
let options = json!({"version": "1.2.3.4.5-beta.1+r.0"});
31+
static TPL: &str = r#"{{ "something" | replace_re: "(some]" }}"#;
32+
static EXPECTED_ERR: &str = r#"
33+
liquid: regex parse error:
34+
(some]
35+
^
36+
error: unclosed group
37+
from: Filter error
38+
with:
39+
filter=replace_re : "(some]"
40+
input="something"
41+
"#;
42+
let err = crate::render_template(TPL, &options).expect_err("template should fail on bad regex");
43+
assert_eq!(err.to_string().trim(), EXPECTED_ERR.trim());
44+
}

crates/spk-schema/crates/liquid/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
mod filter_compare_version;
77
mod filter_parse_version;
8+
mod filter_replace_regex;
89
mod tag_default;
910

1011
pub use liquid::Error;
@@ -19,6 +20,7 @@ pub fn default_parser() -> liquid::Parser {
1920
.tag(tag_default::DefaultTag)
2021
.filter(filter_parse_version::ParseVersion)
2122
.filter(filter_compare_version::CompareVersion)
23+
.filter(filter_replace_regex::ReplaceRegex)
2224
.build();
2325
debug_assert!(matches!(res, Ok(_)), "default template parser is valid");
2426
res.unwrap()

0 commit comments

Comments
 (0)