Skip to content

Commit

Permalink
Add 'FromIterator', change 'From' 'Value' impls
Browse files Browse the repository at this point in the history
Removes the now unnecessary `Value::from_arc_object`.

Adds `FromIterator` impls for:
  * 'Value' for sequences (items of `V: Into<Value>`)
  * `Value` for maps (items of `(K: Into<StaticKey>, V: Into<Value>)`)

Adds 'From' impls to `Value` from:
  * `Arc<T: Object>`
  * `u128`
  * `i128`

Finally documents the new functionality.
  • Loading branch information
SergioBenitez committed Nov 10, 2022
1 parent 7dacfa3 commit f15891f
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 64 deletions.
17 changes: 9 additions & 8 deletions minijinja/src/compiler/ast.rs
Expand Up @@ -390,14 +390,15 @@ 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());
}
}

Some(Value::from(rv))
let items = self.items.iter();
let sequence = items
.filter_map(|expr| match expr {
Expr::Const(v) => Some(v.value.clone()),
_ => None,
})
.collect();

Some(sequence)
}
}

Expand Down
54 changes: 33 additions & 21 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(val).into()
}
}

impl From<u128> for Value {
#[inline(always)]
fn from(val: u128) -> Self {
ValueRepr::U128(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
70 changes: 36 additions & 34 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 @@ -422,39 +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()
}

/// Creates a value from a vector of values.
///
/// Unlike the existing `TryFrom<Vec<T: Into<Value>>>`, this method involves
/// no additional allocations.
///
/// ```rust
/// use std::sync::Arc;
/// # use minijinja::value::Value;
///
/// let values = (0..10).into_iter()
/// .map(Value::from)
/// .collect::<Vec<_>>();
///
/// let v = Value::from_vec(values);
///
/// let values = Arc::new(vec![Value::from("hello")]);
/// let v = Value::from_vec(values);
/// ```
pub fn from_vec<V: Into<Arc<Vec<Value>>>>(values: V) -> Value {
ValueRepr::Seq(values.into()).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 @@ -1013,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
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 f15891f

Please sign in to comment.