Repository: mitsuhiko/minijinja
Pull request #705 (2025-02-06 22:02:00)

Fix plain objects not having an absolute order
Refs #703


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd14a7d..23e9bc3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ All notable changes to MiniJinja are documented here.
 - Added `Environment.undeclared_variables_in_template` and
   `Environnent.undeclared_variables_in_str` to Python binding.  #699
 - Enable `loop_controls` for Python in-line with the CLI.  #704
+- Fixed a panic when comparing plain objects.  #705
 
 ## 2.8.0
 
diff --git a/minijinja/src/value/mod.rs b/minijinja/src/value/mod.rs
index b09eb6c..b7e8ec0 100644
--- a/minijinja/src/value/mod.rs
+++ b/minijinja/src/value/mod.rs
@@ -549,7 +549,12 @@ impl PartialEq for Value {
                                     false
                                 }
                             }
-                            _ => false,
+                            // terrible fallback for plain objects
+                            (ObjectRepr::Plain, ObjectRepr::Plain) => {
+                                a.to_string() == b.to_string()
+                            }
+                            // should not happen
+                            (_, _) => false,
                         }
                     } else {
                         false
@@ -621,6 +626,11 @@ impl Ord for Value {
                                     (Some(a), Some(b)) => a.cmp(b),
                                     _ => unreachable!(),
                                 },
+                                // terrible fallback for plain objects
+                                (ObjectRepr::Plain, ObjectRepr::Plain) => {
+                                    a.to_string().cmp(&b.to_string())
+                                }
+                                // should not happen
                                 (_, _) => unreachable!(),
                             }
                         }


Test patch:
diff --git a/minijinja/tests/test_value.rs b/minijinja/tests/test_value.rs
index 588926a..a2291f8 100644
--- a/minijinja/tests/test_value.rs
+++ b/minijinja/tests/test_value.rs
@@ -1,4 +1,5 @@
 use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList, VecDeque};
+use std::fmt;
 use std::sync::Arc;
 
 use insta::{assert_debug_snapshot, assert_snapshot};
@@ -1216,3 +1217,34 @@ fn test_bytes() {
         "b'\\'foo\"'"
     );
 }
+
+#[test]
+fn test_plain_object_compare() {
+    #[derive(Debug)]
+    struct X(i32);
+
+    impl Object for X {
+        fn repr(self: &Arc<Self>) -> ObjectRepr {
+            ObjectRepr::Plain
+        }
+
+        fn render(self: &Arc<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result
+        where
+            Self: Sized + 'static,
+        {
+            // Today these compare by string fallback
+            write!(f, "{}", self.0)
+        }
+    }
+
+    let v1 = Value::from_object(X(4));
+    let v1_clone = v1.clone();
+    let v2 = Value::from_object(X(0));
+
+    assert!(v1 == v1_clone);
+    assert!((v1 >= v1_clone));
+    assert!(v2 < v1);
+    assert!(v2 != v1);
+    assert!(v1 != Value::from(vec![1, 2, 3]));
+    assert!(v1 > Value::from(vec![1, 2, 3]));
+}