diff --git a/.changes/unreleased/Fixes-20250824-234019.yaml b/.changes/unreleased/Fixes-20250824-234019.yaml new file mode 100644 index 00000000..3d6e60fa --- /dev/null +++ b/.changes/unreleased/Fixes-20250824-234019.yaml @@ -0,0 +1,3 @@ +kind: Fixes +body: Make modules.re methods accept an already compiled pattern +time: 2025-08-24T23:40:19.408475+02:00 diff --git a/crates/dbt-jinja/minijinja-contrib/Cargo.toml b/crates/dbt-jinja/minijinja-contrib/Cargo.toml index 7ce2709e..fa3a0761 100644 --- a/crates/dbt-jinja/minijinja-contrib/Cargo.toml +++ b/crates/dbt-jinja/minijinja-contrib/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "minijinja-contrib" version = "2.5.0" -edition = "2021" +edition = "2024" license = "Apache-2.0" authors = ["Armin Ronacher "] description = "Extra utilities for MiniJinja" @@ -9,7 +9,7 @@ homepage = "https://github.com/mitsuhiko/minijinja" repository = "https://github.com/mitsuhiko/minijinja" keywords = ["jinja", "jinja2", "templates"] readme = "README.md" -rust-version = "1.70" +rust-version = "1.88.0" [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "doc-header.html"] diff --git a/crates/dbt-jinja/minijinja-contrib/src/filters/datetime.rs b/crates/dbt-jinja/minijinja-contrib/src/filters/datetime.rs index 81b83313..86a232e3 100644 --- a/crates/dbt-jinja/minijinja-contrib/src/filters/datetime.rs +++ b/crates/dbt-jinja/minijinja-contrib/src/filters/datetime.rs @@ -2,10 +2,10 @@ use std::convert::TryFrom; use minijinja::value::{Kwargs, Value, ValueKind}; use minijinja::{Error, ErrorKind, State}; -use serde::de::value::SeqDeserializer; use serde::Deserialize; +use serde::de::value::SeqDeserializer; use time::format_description::well_known::iso8601::Iso8601; -use time::{format_description, Date, OffsetDateTime, PrimitiveDateTime}; +use time::{Date, OffsetDateTime, PrimitiveDateTime, format_description}; fn handle_serde_error(err: serde::de::value::Error) -> Error { Error::new(ErrorKind::InvalidOperation, "not a valid date or timestamp").with_source(err) @@ -39,7 +39,7 @@ fn value_to_datetime( ErrorKind::InvalidOperation, "not a valid date or timestamp", ) - .with_source(original_err)) + .with_source(original_err)); } }, }, diff --git a/crates/dbt-jinja/minijinja-contrib/src/filters/mod.rs b/crates/dbt-jinja/minijinja-contrib/src/filters/mod.rs index 39647c13..bef78b4b 100644 --- a/crates/dbt-jinja/minijinja-contrib/src/filters/mod.rs +++ b/crates/dbt-jinja/minijinja-contrib/src/filters/mod.rs @@ -1,7 +1,7 @@ use std::convert::TryFrom; -use minijinja::value::{Kwargs, Value, ValueKind}; use minijinja::State; +use minijinja::value::{Kwargs, Value, ValueKind}; use minijinja::{Error, ErrorKind}; #[cfg(feature = "datetime")] @@ -262,7 +262,7 @@ pub fn wordcount(value: &Value) -> Result { #[cfg(feature = "wordwrap")] #[cfg_attr(docsrs, doc(any(cfg(feature = "wordwrap"), cfg = "unicode_wordwrap")))] pub fn wordwrap(value: &Value, kwargs: Kwargs) -> Result { - use textwrap::{wrap, Options as WrapOptions, WordSplitter}; + use textwrap::{Options as WrapOptions, WordSplitter, wrap}; let s = value.as_str().unwrap_or_default(); let width = kwargs.get::>("width")?.unwrap_or(79); diff --git a/crates/dbt-jinja/minijinja-contrib/src/globals.rs b/crates/dbt-jinja/minijinja-contrib/src/globals.rs index 9b1d50ce..b5848843 100644 --- a/crates/dbt-jinja/minijinja-contrib/src/globals.rs +++ b/crates/dbt-jinja/minijinja-contrib/src/globals.rs @@ -1,11 +1,11 @@ use std::rc::Rc; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use minijinja::listener::RenderingEventListener; #[allow(unused)] use minijinja::value::Value; -use minijinja::value::{from_args, Object, ObjectRepr}; +use minijinja::value::{Object, ObjectRepr, from_args}; use minijinja::{Error, ErrorKind, State}; /// Returns the current time in UTC as unix timestamp. @@ -137,8 +137,8 @@ pub fn joiner(sep: Option) -> Value { /// Returns the rng for the state #[cfg(feature = "rand")] pub(crate) fn get_rng(state: &State) -> rand::rngs::SmallRng { - use rand::rngs::SmallRng; use rand::SeedableRng; + use rand::rngs::SmallRng; if let Some(seed) = state .lookup("RAND_SEED") @@ -189,8 +189,8 @@ pub fn lipsum( n: Option, kwargs: minijinja::value::Kwargs, ) -> Result { - use rand::seq::SliceRandom; use rand::Rng; + use rand::seq::SliceRandom; #[rustfmt::skip] const LIPSUM_WORDS: &[&str] = &[ diff --git a/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/date.rs b/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/date.rs index 1bdf6558..01e41f18 100644 --- a/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/date.rs +++ b/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/date.rs @@ -1,6 +1,6 @@ use chrono::{Datelike, Local, NaiveDate, NaiveDateTime, TimeZone}; use minijinja::arg_utils::ArgParser; -use minijinja::{value::Object, Error, ErrorKind, Value}; +use minijinja::{Error, ErrorKind, Value, value::Object}; use std::fmt; use std::sync::Arc; @@ -336,9 +336,9 @@ impl Object for PyDate { mod tests { use super::*; use crate::modules::py_datetime::timedelta::PyTimeDelta; + use minijinja::Environment; use minijinja::args; use minijinja::context; - use minijinja::Environment; #[test] fn test_date_strftime() { @@ -361,9 +361,11 @@ mod tests { // Test error case - missing format argument let error = date_arc.strftime(&[]).unwrap_err(); - assert!(error - .to_string() - .contains("strftime requires one string argument")); + assert!( + error + .to_string() + .contains("strftime requires one string argument") + ); } #[test] @@ -546,9 +548,11 @@ mod tests { // Test invalid date let result = date_arc.replace(args!(month => 13)); assert!(result.is_err()); - assert!(result - .unwrap_err() - .to_string() - .contains("Invalid date: year=2023, month=13, day=15")); + assert!( + result + .unwrap_err() + .to_string() + .contains("Invalid date: year=2023, month=13, day=15") + ); } } diff --git a/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/datetime.rs b/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/datetime.rs index 0c6086f3..b0164189 100644 --- a/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/datetime.rs +++ b/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/datetime.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc}; use chrono_tz::Tz; -use minijinja::{arg_utils::ArgParser, value::Object, Error, ErrorKind, Value}; +use minijinja::{Error, ErrorKind, Value, arg_utils::ArgParser, value::Object}; use crate::modules::py_datetime::date::PyDate; // your date use crate::modules::py_datetime::time::PyTime; diff --git a/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/time.rs b/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/time.rs index b38f76cf..04294e94 100644 --- a/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/time.rs +++ b/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/time.rs @@ -1,5 +1,5 @@ use chrono::{Local, NaiveDate, NaiveTime, Timelike}; -use minijinja::{arg_utils::ArgParser, value::Object, Error, ErrorKind, Value}; +use minijinja::{Error, ErrorKind, Value, arg_utils::ArgParser, value::Object}; use std::fmt; use std::sync::Arc; @@ -92,7 +92,7 @@ impl PyTimeClass { return Err(Error::new( ErrorKind::InvalidArgument, format!("Invalid iso time format: {iso_str}: {e}"), - )) + )); } }; @@ -318,8 +318,8 @@ impl Object for PyTime { mod tests { use super::*; use crate::modules::py_datetime::timedelta::PyTimeDelta; - use minijinja::context; use minijinja::Environment; + use minijinja::context; #[test] fn test_time_strftime() { @@ -342,9 +342,11 @@ mod tests { // Test error case - missing format argument let error = time_arc.strftime(&[]).unwrap_err(); - assert!(error - .to_string() - .contains("strftime requires one string argument")); + assert!( + error + .to_string() + .contains("strftime requires one string argument") + ); } #[test] diff --git a/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/timedelta.rs b/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/timedelta.rs index 68a54a45..9ce27542 100644 --- a/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/timedelta.rs +++ b/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/timedelta.rs @@ -1,5 +1,5 @@ use chrono::Duration; -use minijinja::{arg_utils::ArgParser, value::Object, Error, ErrorKind, Value}; +use minijinja::{Error, ErrorKind, Value, arg_utils::ArgParser, value::Object}; use std::fmt; use std::sync::Arc; @@ -263,9 +263,9 @@ impl Object for PyTimeDelta { #[cfg(test)] mod tests { use super::*; - use minijinja::args; use minijinja::Environment; use minijinja::Value; + use minijinja::args; #[test] fn test_timedelta_creation() { diff --git a/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/tzinfo.rs b/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/tzinfo.rs index ac65c9ed..a0edd9f5 100644 --- a/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/tzinfo.rs +++ b/crates/dbt-jinja/minijinja-contrib/src/modules/py_datetime/tzinfo.rs @@ -1,4 +1,4 @@ -use minijinja::{value::Object, Error, ErrorKind, Value}; +use minijinja::{Error, ErrorKind, Value, value::Object}; use std::fmt; use std::rc::Rc; use std::sync::Arc; diff --git a/crates/dbt-jinja/minijinja-contrib/src/modules/re.rs b/crates/dbt-jinja/minijinja-contrib/src/modules/re.rs index 75292b22..545e7c68 100644 --- a/crates/dbt-jinja/minijinja-contrib/src/modules/re.rs +++ b/crates/dbt-jinja/minijinja-contrib/src/modules/re.rs @@ -6,7 +6,7 @@ //! pattern-oriented usage consistent with MiniJinja's function/value approach. use fancy_regex::{Captures, Expander, Regex}; // like python regex, fancy_regex supports lookadheds/lookbehinds -use minijinja::{value::Object, Error, ErrorKind, Value}; +use minijinja::{Error, ErrorKind, Value, value::Object}; use std::{collections::BTreeMap, iter, sync::Arc}; /// Create a namespace with `re`-like functions for pattern matching. @@ -295,13 +295,19 @@ fn get_or_compile_regex_and_text(args: &[Value]) -> Result<(Box, &str), E } // First arg: either compiled or raw pattern - let pattern = args[0].to_string(); - let compiled = Box::new(Regex::new(&pattern).map_err(|e| { - Error::new( - ErrorKind::InvalidOperation, - format!("Failed to compile regex: {e}"), - ) - })?); + let compiled = if let Some(object) = args[0].as_object() + && let Some(pattern) = object.downcast_ref::() + { + Box::new(pattern._compiled.clone()) + } else { + let pattern = args[0].to_string(); + Box::new(Regex::new(&pattern).map_err(|e| { + Error::new( + ErrorKind::InvalidOperation, + format!("Failed to compile regex: {e}"), + ) + })?) + }; // Second arg: the text to match against let text = args[1].to_string(); @@ -413,4 +419,18 @@ mod tests { .unwrap(); assert!(!result.is_true()); } + + #[test] + fn test_re_search() { + let result = re_search(&[ + Value::from(".*".to_string()), + Value::from("xyz".to_string()), + ]) + .unwrap(); + assert!(result.is_true()); + + let compiled_pattern = re_compile(&[Value::from(".*".to_string())]).unwrap(); + let result = re_search(&[compiled_pattern, Value::from("xyz".to_string())]).unwrap(); + assert!(result.is_true()); + } } diff --git a/crates/dbt-jinja/minijinja-contrib/src/pycompat.rs b/crates/dbt-jinja/minijinja-contrib/src/pycompat.rs index 25101ff5..0a1114b9 100644 --- a/crates/dbt-jinja/minijinja-contrib/src/pycompat.rs +++ b/crates/dbt-jinja/minijinja-contrib/src/pycompat.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; use minijinja::tuple; use minijinja::value::mutable_vec::MutableVec; -use minijinja::value::{from_args, ValueKind}; +use minijinja::value::{ValueKind, from_args}; use minijinja::{Error, ErrorKind, State, Value}; use regex::Regex; diff --git a/crates/dbt-jinja/minijinja-contrib/tests/filters.rs b/crates/dbt-jinja/minijinja-contrib/tests/filters.rs index 6c459c02..f79677e2 100644 --- a/crates/dbt-jinja/minijinja-contrib/tests/filters.rs +++ b/crates/dbt-jinja/minijinja-contrib/tests/filters.rs @@ -1,4 +1,4 @@ -use minijinja::{context, Environment}; +use minijinja::{Environment, context}; use minijinja_contrib::filters::pluralize; use similar_asserts::assert_eq; diff --git a/crates/dbt-jinja/minijinja-contrib/tests/globals.rs b/crates/dbt-jinja/minijinja-contrib/tests/globals.rs index ac01eb19..e672221f 100644 --- a/crates/dbt-jinja/minijinja-contrib/tests/globals.rs +++ b/crates/dbt-jinja/minijinja-contrib/tests/globals.rs @@ -1,5 +1,5 @@ use insta::assert_snapshot; -use minijinja::{render, Environment}; +use minijinja::{Environment, render}; use minijinja_contrib::globals::{cycler, joiner}; #[test] diff --git a/crates/dbt-jinja/minijinja-contrib/tests/validation.rs b/crates/dbt-jinja/minijinja-contrib/tests/validation.rs index 73ba5608..c4e75286 100644 --- a/crates/dbt-jinja/minijinja-contrib/tests/validation.rs +++ b/crates/dbt-jinja/minijinja-contrib/tests/validation.rs @@ -10,27 +10,35 @@ fn test_validation() { let mut env = Environment::new(); env.add_global("validation", create_validation_namespace()); - assert!(eval_expr( - &env, - "validation.any['compound', 'interleaved']('compound')" - ) - .unwrap() - .is_true()); - assert!(eval_expr( - &env, - "validation.any['compound', 'interleaved']('interleaved')" - ) - .unwrap() - .is_true()); - assert!(eval_expr( - &env, - "validation.any['compound', 'interleaved']('something_else')" - ) - .is_err()); - - assert!(eval_expr(&env, "validation.any['compound']('compound')") + assert!( + eval_expr( + &env, + "validation.any['compound', 'interleaved']('compound')" + ) + .unwrap() + .is_true() + ); + assert!( + eval_expr( + &env, + "validation.any['compound', 'interleaved']('interleaved')" + ) .unwrap() - .is_true()); + .is_true() + ); + assert!( + eval_expr( + &env, + "validation.any['compound', 'interleaved']('something_else')" + ) + .is_err() + ); + + assert!( + eval_expr(&env, "validation.any['compound']('compound')") + .unwrap() + .is_true() + ); assert!(eval_expr(&env, "validation.any['compound']('something_else')").is_err()); // compound here is a type, we should always return true when type is used assert!( @@ -38,10 +46,14 @@ fn test_validation() { .unwrap() .is_true() ); - assert!(eval_expr(&env, "validation.any[anytype](1)") - .unwrap() - .is_true()); - assert!(eval_expr(&env, "validation.any['test', anytype](1)") - .unwrap() - .is_true()); + assert!( + eval_expr(&env, "validation.any[anytype](1)") + .unwrap() + .is_true() + ); + assert!( + eval_expr(&env, "validation.any['test', anytype](1)") + .unwrap() + .is_true() + ); }