From 1b026d0c1f096f46ebe3ce4af308a2ff3f0c7ca5 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 31 Oct 2024 13:14:27 +0100 Subject: [PATCH] Implement sameas test --- minijinja/src/defaults.rs | 1 + minijinja/src/tests.rs | 30 +++++++++++++++++++ minijinja/tests/inputs/tests.txt | 5 +++- .../test_templates__vm@debug.txt.snap | 1 + .../test_templates__vm@tests.txt.snap | 5 +++- 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/minijinja/src/defaults.rs b/minijinja/src/defaults.rs index ffc7ba3e..20894ddc 100644 --- a/minijinja/src/defaults.rs +++ b/minijinja/src/defaults.rs @@ -143,6 +143,7 @@ pub(crate) fn get_builtin_tests() -> BTreeMap, BoxedTest> { rv.insert("endingwith".into(), BoxedTest::new(tests::is_endingwith)); rv.insert("lower".into(), BoxedTest::new(tests::is_lower)); rv.insert("upper".into(), BoxedTest::new(tests::is_upper)); + rv.insert("sameas".into(), BoxedTest::new(tests::is_sameas)); // operators let is_eq = BoxedTest::new(tests::is_eq); diff --git a/minijinja/src/tests.rs b/minijinja/src/tests.rs index f3528a82..088b6f76 100644 --- a/minijinja/src/tests.rs +++ b/minijinja/src/tests.rs @@ -565,6 +565,36 @@ mod builtins { pub fn is_upper(name: &str) -> bool { name.chars().all(|x| x.is_uppercase()) } + + /// Checks if two values are identical. + /// + /// This primarily exists for compatibilith with Jinja2. It can be seen as a much + /// stricter comparison than a regular comparison. The main difference is that + /// values that have the same structure but a different internal object will not + /// compare equal. + /// + /// ```jinja + /// {{ [1, 2, 3] is sameas([1, 2, 3]) }} + /// -> false + /// + /// {{ false is sameas(false) }} + /// -> true + /// ``` + #[cfg_attr(docsrs, doc(cfg(feature = "builtins")))] + #[cfg(feature = "builtins")] + pub fn is_sameas(value: &Value, other: &Value) -> bool { + match (value.as_object(), other.as_object()) { + (Some(a), Some(b)) => a.is_same_object(b), + (None, Some(_)) | (Some(_), None) => false, + (None, None) => { + if value.kind() != other.kind() || value.is_integer() != other.is_integer() { + false + } else { + value == other + } + } + } + } } #[cfg(feature = "builtins")] diff --git a/minijinja/tests/inputs/tests.txt b/minijinja/tests/inputs/tests.txt index 788eeb25..725a9eea 100644 --- a/minijinja/tests/inputs/tests.txt +++ b/minijinja/tests/inputs/tests.txt @@ -48,4 +48,7 @@ is-test: {{ 'safe' is test }} | {{ 'unknown-test' is test }} is-boolean: {{ true is boolean }} | {{ 42 is boolean }} is-divisibleby: {{ 42 is divisibleby(2) }} | {{ 41 is divisibleby(2) }} is-lower: {{ "foo" is lower }} | {{ "FOO" is lower }} -is-upper: {{ "foo" is upper }} | {{ "FOO" is upper }} \ No newline at end of file +is-upper: {{ "foo" is upper }} | {{ "FOO" is upper }} +seq-same-as: {{ [1, 2, 3] is sameas([1, 2, 3]) }} +const-same-as: {{ true is sameas(true) }} +int-same-as: {{ 1 is sameas(1.0) }} \ No newline at end of file diff --git a/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap b/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap index 64ef19d4..007fdcb0 100644 --- a/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap @@ -64,6 +64,7 @@ State { "number", "odd", "safe", + "sameas", "sequence", "startingwith", "string", diff --git a/minijinja/tests/snapshots/test_templates__vm@tests.txt.snap b/minijinja/tests/snapshots/test_templates__vm@tests.txt.snap index 2fe01111..4378c3d5 100644 --- a/minijinja/tests/snapshots/test_templates__vm@tests.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@tests.txt.snap @@ -1,6 +1,6 @@ --- source: minijinja/tests/test_templates.rs -description: "even: {{ two is even }}\nodd: {{ two is odd }}\nundefined: {{ two is undefined }}\ndefined: {{ two is defined }}\nundefined2: {{ ohwell is undefined }}\ndefined2: {{ ohwell is defined }}\nnone: {{ none is none }}\nnot-none: {{ 42 is not none }}\nnumber-int: {{ two is number }}\nnumber-float: {{ two_dot_two is number }}\ninteger-int: {{ 42 is integer }}\ninteger-float: {{ 42.0 is integer }}\nfloat-int: {{ 42 is float }}\nfloat-float: {{ 42.0 is float }}\nnot-seq: {{ two is sequence }}\nseq: {{ seq is sequence }}\nreverse-not-seq: {{ seq|reverse is sequence }}\niterable: {{ seq is iterable }}\niterable-reverse: {{ seq|reverse is iterable }}\nstring-iterable: {{ string is iterable }}\nnot-iterable: {{ two is iterable }}\nnot-map: {{ two is mapping }}\nmap: {{ map is mapping }}\nstring: {{ string is string }}\nnot-string: {{ mapping is string }}\nstarts-with-a: {{ string is startingwith('a') }}\nends-with-ha: {{ string is endingwith('ha') }}\nnot-safe: {{ \"foo\" is safe }}\nsafe: {{ \"foo\"|escape is safe }}\nis-true: {{ true is true }} | {{ 42 is true }}\nis-false: {{ false is false }} | {{ 0 is false }}\nis-filter: {{ 'escape' is filter }} | {{ 'unknown-filter' is filter }}\nis-test: {{ 'safe' is test }} | {{ 'unknown-test' is test }}\nis-boolean: {{ true is boolean }} | {{ 42 is boolean }}\nis-divisibleby: {{ 42 is divisibleby(2) }} | {{ 41 is divisibleby(2) }}\nis-lower: {{ \"foo\" is lower }} | {{ \"FOO\" is lower }}\nis-upper: {{ \"foo\" is upper }} | {{ \"FOO\" is upper }}" +description: "even: {{ two is even }}\nodd: {{ two is odd }}\nundefined: {{ two is undefined }}\ndefined: {{ two is defined }}\nundefined2: {{ ohwell is undefined }}\ndefined2: {{ ohwell is defined }}\nnone: {{ none is none }}\nnot-none: {{ 42 is not none }}\nnumber-int: {{ two is number }}\nnumber-float: {{ two_dot_two is number }}\ninteger-int: {{ 42 is integer }}\ninteger-float: {{ 42.0 is integer }}\nfloat-int: {{ 42 is float }}\nfloat-float: {{ 42.0 is float }}\nnot-seq: {{ two is sequence }}\nseq: {{ seq is sequence }}\nreverse-not-seq: {{ seq|reverse is sequence }}\niterable: {{ seq is iterable }}\niterable-reverse: {{ seq|reverse is iterable }}\nstring-iterable: {{ string is iterable }}\nnot-iterable: {{ two is iterable }}\nnot-map: {{ two is mapping }}\nmap: {{ map is mapping }}\nstring: {{ string is string }}\nnot-string: {{ mapping is string }}\nstarts-with-a: {{ string is startingwith('a') }}\nends-with-ha: {{ string is endingwith('ha') }}\nnot-safe: {{ \"foo\" is safe }}\nsafe: {{ \"foo\"|escape is safe }}\nis-true: {{ true is true }} | {{ 42 is true }}\nis-false: {{ false is false }} | {{ 0 is false }}\nis-filter: {{ 'escape' is filter }} | {{ 'unknown-filter' is filter }}\nis-test: {{ 'safe' is test }} | {{ 'unknown-test' is test }}\nis-boolean: {{ true is boolean }} | {{ 42 is boolean }}\nis-divisibleby: {{ 42 is divisibleby(2) }} | {{ 41 is divisibleby(2) }}\nis-lower: {{ \"foo\" is lower }} | {{ \"FOO\" is lower }}\nis-upper: {{ \"foo\" is upper }} | {{ \"FOO\" is upper }}\nseq-same-as: {{ [1, 2, 3] is sameas([1, 2, 3]) }}\nconst-same-as: {{ true is sameas(true) }}\nint-same-as: {{ 1 is sameas(1.0) }}" info: two: 2 two_dot_two: 2.2 @@ -50,3 +50,6 @@ is-boolean: true | false is-divisibleby: true | false is-lower: true | false is-upper: false | true +seq-same-as: false +const-same-as: true +int-same-as: false