Skip to content

Commit

Permalink
Improvements to value conversion API (#142)
Browse files Browse the repository at this point in the history
  • Loading branch information
SergioBenitez committed Nov 10, 2022
1 parent ac738f1 commit 3241040
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 59 deletions.
13 changes: 6 additions & 7 deletions minijinja/src/compiler/ast.rs
Expand Up @@ -390,14 +390,13 @@ impl<'a> List<'a> {
return None;
}

let mut rv = Vec::new();
for expr in &self.items {
if let Expr::Const(val) = expr {
rv.push(val.value.clone());
}
}
let items = self.items.iter();
let sequence = items.filter_map(|expr| match expr {
Expr::Const(v) => Some(v.value.clone()),
_ => None,
});

Some(Value::from(rv))
Some(sequence.collect())
}
}

Expand Down
4 changes: 2 additions & 2 deletions minijinja/src/key/mod.rs
Expand Up @@ -82,11 +82,11 @@ impl<'a> Key<'a> {
ValueRepr::U64(v) => TryFrom::try_from(v)
.map(Key::I64)
.map_err(|_| ErrorKind::NonKey.into()),
ValueRepr::U128(ref v) => TryFrom::try_from(**v)
ValueRepr::U128(v) => TryFrom::try_from(v)
.map(Key::I64)
.map_err(|_| ErrorKind::NonKey.into()),
ValueRepr::I64(v) => Ok(Key::I64(v)),
ValueRepr::I128(ref v) => TryFrom::try_from(**v)
ValueRepr::I128(v) => TryFrom::try_from(v)
.map(Key::I64)
.map_err(|_| ErrorKind::NonKey.into()),
ValueRepr::F64(x) => {
Expand Down
58 changes: 35 additions & 23 deletions minijinja/src/value/argtypes.rs
Expand Up @@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut};

use crate::error::{Error, ErrorKind};
use crate::key::{Key, StaticKey};
use crate::value::{Arc, MapType, StringType, Value, ValueKind, ValueRepr};
use crate::value::{Arc, MapType, Object, StringType, Value, ValueKind, ValueRepr};
use crate::vm::State;

/// A utility trait that represents the return value of functions and filters.
Expand Down Expand Up @@ -231,20 +231,6 @@ impl From<()> for Value {
}
}

impl From<i128> for Value {
#[inline(always)]
fn from(val: i128) -> Self {
ValueRepr::I128(Arc::new(val)).into()
}
}

impl From<u128> for Value {
#[inline(always)]
fn from(val: u128) -> Self {
ValueRepr::U128(Arc::new(val)).into()
}
}

impl<'a> From<Key<'a>> for Value {
fn from(val: Key) -> Self {
match val {
Expand All @@ -257,19 +243,40 @@ impl<'a> From<Key<'a>> for Value {
}
}

impl<V: Into<Value>> FromIterator<V> for Value {
fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
let vec = iter.into_iter().map(|v| v.into()).collect();

ValueRepr::Seq(Arc::new(vec)).into()
}
}

impl<K: Into<StaticKey>, V: Into<Value>> FromIterator<(K, V)> for Value {
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
let map = iter
.into_iter()
.map(|(k, v)| (k.into(), v.into()))
.collect();

ValueRepr::Map(Arc::new(map), MapType::Normal).into()
}
}

impl<K: Into<StaticKey>, V: Into<Value>> From<BTreeMap<K, V>> for Value {
fn from(val: BTreeMap<K, V>) -> Self {
ValueRepr::Map(
Arc::new(val.into_iter().map(|(k, v)| (k.into(), v.into())).collect()),
MapType::Normal,
)
.into()
val.into_iter().map(|(k, v)| (k.into(), v.into())).collect()
}
}

impl<T: Into<Value>> From<Vec<T>> for Value {
fn from(val: Vec<T>) -> Self {
ValueRepr::Seq(Arc::new(val.into_iter().map(|x| x.into()).collect())).into()
val.into_iter().map(|v| v.into()).collect()
}
}

impl<T: Object> From<Arc<T>> for Value {
fn from(object: Arc<T>) -> Self {
Value::from(object as Arc<dyn Object>)
}
}

Expand All @@ -289,13 +296,18 @@ value_from!(u8, U64);
value_from!(u16, U64);
value_from!(u32, U64);
value_from!(u64, U64);
value_from!(u128, U128);
value_from!(i8, I64);
value_from!(i16, I64);
value_from!(i32, I64);
value_from!(i64, I64);
value_from!(i128, I128);
value_from!(f32, F64);
value_from!(f64, F64);
value_from!(char, Char);
value_from!(Arc<Vec<u8>>, Bytes);
value_from!(Arc<Vec<Value>>, Seq);
value_from!(Arc<dyn Object>, Dynamic);

fn unsupported_conversion(kind: ValueKind, target: &str) -> Error {
Error::new(
Expand Down Expand Up @@ -339,8 +351,8 @@ macro_rules! primitive_int_try_from {
ValueRepr::U64(val) => val,
// for the intention here see Key::from_borrowed_value
ValueRepr::F64(val) if (val as i64 as f64 == val) => val as i64,
ValueRepr::I128(ref val) => **val,
ValueRepr::U128(ref val) => **val,
ValueRepr::I128(val) => val,
ValueRepr::U128(val) => val,
});
}
}
Expand Down
64 changes: 44 additions & 20 deletions minijinja/src/value/mod.rs
Expand Up @@ -18,6 +18,17 @@
//! let value = Value::from(42);
//! ```
//!
//! Or via the [`FromIterator`] trait:
//!
//! ```
//! # use minijinja::value::Value;
//! // collection into a sequence
//! let value: Value = (1..10).into_iter().collect();
//!
//! // collection into a map
//! let value: Value = [("key", "value")].into_iter().collect();
//! ```
//!
//! MiniJinja will however create values via an indirection via [`serde`] when
//! a template is rendered or an expression is evaluated. This can also be
//! triggered manually by using the [`Value::from_serializable`] method:
Expand Down Expand Up @@ -62,6 +73,28 @@
//! [`Object`] trait. These can be used to implement dynamic functionality such as
//! stateful values and more. Dynamic objects are internally also used to implement
//! the special `loop` variable or macros.
//!
//! To create a dynamic `Value` object, use [`Value::from_object()`] or the
//! `From<Arc<T: Object>>` implementations for `Value`:
//!
//! ```rust
//! # use std::sync::Arc;
//! # use minijinja::value::{Value, Object};
//! #[derive(Debug)]
//! struct Foo;
//!
//! # impl std::fmt::Display for Foo {
//! # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) }
//! # }
//! #
//! impl Object for Foo {
//! /* implementation */
//! }
//!
//! let value = Value::from_object(Foo);
//! let value = Value::from(Arc::new(Foo));
//! let value = Value::from(Arc::new(Foo) as Arc<dyn Object>);
//! ```

// this module is based on the content module in insta which in turn is based
// on the content module in serde::private::ser.
Expand Down Expand Up @@ -230,8 +263,8 @@ pub(crate) enum ValueRepr {
F64(f64),
Char(char),
None,
U128(Arc<u128>),
I128(Arc<i128>),
U128(u128),
I128(i128),
String(Arc<String>, StringType),
Bytes(Arc<Vec<u8>>),
Seq(Arc<Vec<Value>>),
Expand Down Expand Up @@ -422,17 +455,8 @@ impl Value {
///
/// let val = Value::from_object(Thing { id: 42 });
/// ```
pub fn from_object<T: Object + 'static>(value: T) -> Value {
Value::from_arc_object(Arc::new(value))
}

/// Creates a value from a reference counted dynamic object.
///
/// As values are internally holding dynamic objects in an `Arc` it can be
/// convenient to hold on to it so that the ownership can be shared between
/// the boxed value and outside.
pub fn from_arc_object<T: Object + 'static>(value: Arc<T>) -> Value {
ValueRepr::Dynamic(value as Arc<dyn Object>).into()
pub fn from_object<T: Object>(value: T) -> Value {
Value::from(Arc::new(value) as Arc<dyn Object>)
}

/// Creates a callable value from a function.
Expand Down Expand Up @@ -482,9 +506,9 @@ impl Value {
match self.0 {
ValueRepr::Bool(val) => val,
ValueRepr::U64(x) => x != 0,
ValueRepr::U128(ref x) => **x != 0,
ValueRepr::U128(x) => x != 0,
ValueRepr::I64(x) => x != 0,
ValueRepr::I128(ref x) => **x != 0,
ValueRepr::I128(x) => x != 0,
ValueRepr::F64(x) => x != 0.0,
ValueRepr::Char(x) => x != '\x00',
ValueRepr::String(ref x, _) => !x.is_empty(),
Expand Down Expand Up @@ -779,11 +803,11 @@ impl Value {
ValueRepr::U64(v) => TryFrom::try_from(v)
.map(Key::I64)
.map_err(|_| ErrorKind::NonKey.into()),
ValueRepr::U128(ref v) => TryFrom::try_from(**v)
ValueRepr::U128(v) => TryFrom::try_from(v)
.map(Key::I64)
.map_err(|_| ErrorKind::NonKey.into()),
ValueRepr::I64(v) => Ok(Key::I64(v)),
ValueRepr::I128(ref v) => TryFrom::try_from(**v)
ValueRepr::I128(v) => TryFrom::try_from(v)
.map(Key::I64)
.map_err(|_| ErrorKind::NonKey.into()),
ValueRepr::Char(c) => Ok(Key::Char(c)),
Expand Down Expand Up @@ -857,8 +881,8 @@ impl Serialize for Value {
ValueRepr::Char(c) => serializer.serialize_char(c),
ValueRepr::None => serializer.serialize_unit(),
ValueRepr::Undefined => serializer.serialize_unit(),
ValueRepr::U128(ref u) => serializer.serialize_u128(**u),
ValueRepr::I128(ref i) => serializer.serialize_i128(**i),
ValueRepr::U128(u) => serializer.serialize_u128(u),
ValueRepr::I128(i) => serializer.serialize_i128(i),
ValueRepr::String(ref s, _) => serializer.serialize_str(s),
ValueRepr::Bytes(ref b) => serializer.serialize_bytes(b),
ValueRepr::Seq(ref elements) => elements.serialize(serializer),
Expand Down Expand Up @@ -991,7 +1015,7 @@ fn test_dynamic_object_roundtrip() {
}

let x = Arc::new(X(Default::default()));
let x_value = Value::from_arc_object(x.clone());
let x_value = Value::from(x.clone());
x.0.fetch_add(42, atomic::Ordering::Relaxed);
let x_clone = Value::from_serializable(&x_value);
x.0.fetch_add(23, atomic::Ordering::Relaxed);
Expand Down
8 changes: 4 additions & 4 deletions minijinja/src/value/ops.rs
Expand Up @@ -14,9 +14,9 @@ fn as_f64(value: &Value) -> Option<f64> {
Some(match value.0 {
ValueRepr::Bool(x) => x as i64 as f64,
ValueRepr::U64(x) => x as f64,
ValueRepr::U128(ref x) => **x as f64,
ValueRepr::U128(x) => x as f64,
ValueRepr::I64(x) => x as f64,
ValueRepr::I128(ref x) => **x as f64,
ValueRepr::I128(x) => x as f64,
ValueRepr::F64(x) => x,
_ => return None,
})
Expand All @@ -27,13 +27,13 @@ pub fn coerce(a: &Value, b: &Value) -> Option<CoerceResult> {
// equal mappings are trivial
(ValueRepr::U64(a), ValueRepr::U64(b)) => Some(CoerceResult::I128(*a as i128, *b as i128)),
(ValueRepr::U128(a), ValueRepr::U128(b)) => {
Some(CoerceResult::I128(**a as i128, **b as i128))
Some(CoerceResult::I128(*a as i128, *b as i128))
}
(ValueRepr::String(a, _), ValueRepr::String(b, _)) => {
Some(CoerceResult::String(a.to_string(), b.to_string()))
}
(ValueRepr::I64(a), ValueRepr::I64(b)) => Some(CoerceResult::I128(*a as i128, *b as i128)),
(ValueRepr::I128(ref a), ValueRepr::I128(ref b)) => Some(CoerceResult::I128(**a, **b)),
(ValueRepr::I128(a), ValueRepr::I128(b)) => Some(CoerceResult::I128(*a, *b)),
(ValueRepr::F64(a), ValueRepr::F64(b)) => Some(CoerceResult::F64(*a, *b)),

// are floats involved?
Expand Down
4 changes: 2 additions & 2 deletions minijinja/src/value/serialize.rs
Expand Up @@ -43,7 +43,7 @@ impl Serializer for ValueSerializer {
}

fn serialize_i128(self, v: i128) -> Result<Value, Error> {
Ok(ValueRepr::I128(Arc::new(v)).into())
Ok(ValueRepr::I128(v).into())
}

fn serialize_u8(self, v: u8) -> Result<Value, Error> {
Expand All @@ -63,7 +63,7 @@ impl Serializer for ValueSerializer {
}

fn serialize_u128(self, v: u128) -> Result<Value, Error> {
Ok(ValueRepr::U128(Arc::new(v)).into())
Ok(ValueRepr::U128(v).into())
}

fn serialize_f32(self, v: f32) -> Result<Value, Error> {
Expand Down
2 changes: 1 addition & 1 deletion minijinja/src/vm/context.rs
Expand Up @@ -179,7 +179,7 @@ impl<'env> Context<'env> {
// if we are a loop, check if we are looking up the special loop var.
if let Some(ref l) = frame.current_loop {
if l.with_loop_var && key == "loop" {
return Some(Value::from_arc_object(l.object.clone()));
return Some(Value::from(l.object.clone()));
}
}

Expand Down

0 comments on commit 3241040

Please sign in to comment.