Skip to content

Commit

Permalink
Add filter to perform replacements via regular expressions
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Bottriell <[email protected]>
  • Loading branch information
rydrman committed Oct 3, 2022
1 parent c2dac9a commit d4b4cdd
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 0 deletions.
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.

1 change: 1 addition & 0 deletions crates/spk-schema/crates/liquid/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ migration-to-components = ["spk-schema-foundation/migration-to-components"]
[dependencies]
liquid = "0.26.0"
liquid-core = "0.26.0"
regex = "1.6.0"
serde = "1.0"
serde_json = "1.0"
spk-schema-foundation = { path = "../foundation" }
Expand Down
64 changes: 64 additions & 0 deletions crates/spk-schema/crates/liquid/src/filter_replace_regex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) Sony Pictures Imageworks, et al.
// SPDX-License-Identifier: Apache-2.0
// https://github.com/imageworks/spk

use liquid::ValueView;
use liquid_core::{
Display_filter,
Expression,
Filter,
FilterParameters,
FilterReflection,
FromFilterParameters,
ParseFilter,
Result,
Runtime,
Value,
};

#[cfg(test)]
#[path = "./filter_replace_regex_test.rs"]
mod filter_replace_regex_test;

#[derive(Debug, FilterParameters)]
struct ReplaceRegexArgs {
#[parameter(description = "The regular expression to search.", arg_type = "str")]
search: Expression,
#[parameter(
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>`",
arg_type = "str"
)]
replace: Option<Expression>,
}

#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
name = "replace_re",
description = "Like `replace`, but searches using a regular expression.",
parameters(ReplaceRegexArgs),
parsed(ReplaceRegexFilter)
)]
pub struct ReplaceRegex;

#[derive(Debug, FromFilterParameters, Display_filter)]
#[name = "replace_re"]
struct ReplaceRegexFilter {
#[parameters]
args: ReplaceRegexArgs,
}

impl Filter for ReplaceRegexFilter {
fn evaluate(&self, input: &dyn ValueView, runtime: &dyn Runtime) -> Result<Value> {
let args = self.args.evaluate(runtime)?;

let input = input.to_kstr();

let search = regex::Regex::new(&args.search)
.map_err(|err| liquid::Error::with_msg(err.to_string()))?;
let replace = args.replace.unwrap_or_else(|| "".into());

Ok(Value::scalar(
search.replace_all(&input, replace.as_str()).to_string(),
))
}
}
44 changes: 44 additions & 0 deletions crates/spk-schema/crates/liquid/src/filter_replace_regex_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Sony Pictures Imageworks, et al.
// SPDX-License-Identifier: Apache-2.0
// https://github.com/imageworks/spk

use rstest::rstest;
use serde_json::json;

#[rstest]
fn test_replace_regex_basic() {
let options = json!({});
static TPL: &str = r#"{{ "1992-02-25" | replace_re: "(\d+)-(\d+)-(\d+)", "$3/$2/$1" }}"#;
static EXPECTED: &str = r#"25/02/1992"#;
let rendered =
crate::render_template(TPL, &options).expect("template should not fail to render");
assert_eq!(rendered, EXPECTED);
}

#[rstest]
fn test_replace_regex_empty() {
let options = json!({});
static TPL: &str = r#"{{ "Hello, World!" | replace_re: "[A-Z]" }}"#;
static EXPECTED: &str = r#"ello, orld!"#;
let rendered =
crate::render_template(TPL, &options).expect("template should not fail to render");
assert_eq!(rendered, EXPECTED);
}

#[rstest]
fn test_replace_regex_compile_error() {
let options = json!({"version": "1.2.3.4.5-beta.1+r.0"});
static TPL: &str = r#"{{ "something" | replace_re: "(some]" }}"#;
static EXPECTED_ERR: &str = r#"
liquid: regex parse error:
(some]
^
error: unclosed group
from: Filter error
with:
filter=replace_re : "(some]"
input="something"
"#;
let err = crate::render_template(TPL, &options).expect_err("template should fail on bad regex");
assert_eq!(err.to_string().trim(), EXPECTED_ERR.trim());
}
2 changes: 2 additions & 0 deletions crates/spk-schema/crates/liquid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
mod filter_compare_version;
mod filter_parse_version;
mod filter_replace_regex;
mod tag_default;

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

0 comments on commit d4b4cdd

Please sign in to comment.