Skip to content

Commit

Permalink
Fix flatten error condition for latest serde
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed Jan 2, 2025
1 parent ba620dc commit 526a379
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 28 deletions.
15 changes: 8 additions & 7 deletions minijinja/src/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ use std::fmt;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex};

use serde::ser::{Serialize, Serializer};
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};

use crate::error::{Error, ErrorKind};
use crate::functions;
Expand Down Expand Up @@ -1592,17 +1592,18 @@ impl Serialize for Value {
// we are okay with overflowing the handle here because these values only
// live for a very short period of time and it's not likely that you run out
// of an entire u32 worth of handles in a single serialization operation.
// This lets us stick the handle into a unit variant in the serde data model.
let rv = x.get().wrapping_add(1);
x.set(rv);
rv
});
VALUE_HANDLES.with(|handles| handles.borrow_mut().insert(handle, self.clone()));
return serializer.serialize_unit_variant(
VALUE_HANDLE_MARKER,
handle,
VALUE_HANDLE_MARKER,
);

// we serialize this into a tuple struct as a form of in-band signalling
// we can detect. This also will fail with a somewhat acceptable error
// for flattening operations. See https://github.com/mitsuhiko/minijinja/issues/222
let mut s = ok!(serializer.serialize_tuple_struct(VALUE_HANDLE_MARKER, 1));
ok!(s.serialize_field(&handle));
return s.end();
}

match self.0 {
Expand Down
46 changes: 27 additions & 19 deletions minijinja/src/value/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,20 +140,11 @@ impl Serializer for ValueSerializer {

fn serialize_unit_variant(
self,
name: &'static str,
variant_index: u32,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Value, InvalidValue> {
if name == VALUE_HANDLE_MARKER && variant == VALUE_HANDLE_MARKER {
Ok(VALUE_HANDLES.with(|handles| {
let mut handles = handles.borrow_mut();
handles
.remove(&variant_index)
.expect("value handle not in registry")
}))
} else {
Ok(Value::from(variant))
}
Ok(Value::from(variant))
}

fn serialize_newtype_struct<T>(
Expand Down Expand Up @@ -196,11 +187,13 @@ impl Serializer for ValueSerializer {

fn serialize_tuple_struct(
self,
_name: &'static str,
name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, InvalidValue> {
Ok(SerializeTupleStruct {
fields: Vec::with_capacity(untrusted_size_hint(len)),
Ok(if name == VALUE_HANDLE_MARKER {
SerializeTupleStruct::Handle(None)
} else {
SerializeTupleStruct::Fields(Vec::with_capacity(untrusted_size_hint(len)))
})
}

Expand Down Expand Up @@ -290,8 +283,9 @@ impl ser::SerializeTuple for SerializeTuple {
}
}

pub struct SerializeTupleStruct {
fields: Vec<Value>,
pub enum SerializeTupleStruct {
Handle(Option<u32>),
Fields(Vec<Value>),
}

impl ser::SerializeTupleStruct for SerializeTupleStruct {
Expand All @@ -302,12 +296,26 @@ impl ser::SerializeTupleStruct for SerializeTupleStruct {
where
T: Serialize + ?Sized,
{
self.fields.push(transform(value));
match self {
SerializeTupleStruct::Handle(ref mut handle) => {
*handle = transform(value).as_usize().map(|x| x as u32);
}
SerializeTupleStruct::Fields(ref mut fields) => {
fields.push(transform(value));
}
}
Ok(())
}

fn end(self) -> Result<Value, InvalidValue> {
Ok(Value::from_object(self.fields))
match self {
SerializeTupleStruct::Handle(handle) => VALUE_HANDLES.with(|handles| {
handle
.and_then(|h| handles.borrow_mut().remove(&h))
.ok_or_else(|| InvalidValue("value handle not in registry".into()))
}),
SerializeTupleStruct::Fields(fields) => Ok(Value::from_object(fields)),
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions minijinja/tests/test_templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ fn test_flattening_sub_item_bad_attr() {
assert_eq!(err.kind(), ErrorKind::BadSerialization);
assert_eq!(
err.detail(),
Some("can only flatten structs and maps (got an enum)")
Some("can only flatten structs and maps (got a tuple struct)")
);
}

Expand All @@ -412,7 +412,7 @@ fn test_flattening_sub_item_shielded_print() {
let value = env.render_str("{{ good }}", ctx).unwrap();
assert_eq!(
value,
r#"{"bad": <invalid value: could not serialize to value: can only flatten structs and maps (got an enum)>}"#
r#"{"bad": <invalid value: could not serialize to value: can only flatten structs and maps (got a tuple struct)>}"#
);
}

Expand Down

0 comments on commit 526a379

Please sign in to comment.