From 3b047bd93626b6f11b4e609636eb4d4613264dee Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 9 Nov 2022 15:54:18 -0800 Subject: [PATCH] Add 'FromIterator', change 'From' 'Value' impls Removes the now unnecessary `Value::from_arc_object`. Adds `FromIterator` impls for: * 'Value' for sequences (items of `V: Into`) * `Value` for maps (items of `(K: Into, V: Into)`) Adds 'From' impls to `Value` from: * `Arc` * `u128` * `i128` Finally documents the new functionality. --- minijinja/src/compiler/ast.rs | 13 ++++---- minijinja/src/value/argtypes.rs | 54 ++++++++++++++++++++------------- minijinja/src/value/mod.rs | 48 +++++++++++++++++++++-------- minijinja/src/vm/context.rs | 2 +- 4 files changed, 76 insertions(+), 41 deletions(-) diff --git a/minijinja/src/compiler/ast.rs b/minijinja/src/compiler/ast.rs index 165ada4a..a530970f 100644 --- a/minijinja/src/compiler/ast.rs +++ b/minijinja/src/compiler/ast.rs @@ -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()) } } diff --git a/minijinja/src/value/argtypes.rs b/minijinja/src/value/argtypes.rs index d147d970..a2104279 100644 --- a/minijinja/src/value/argtypes.rs +++ b/minijinja/src/value/argtypes.rs @@ -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. @@ -231,20 +231,6 @@ impl From<()> for Value { } } -impl From for Value { - #[inline(always)] - fn from(val: i128) -> Self { - ValueRepr::I128(val).into() - } -} - -impl From for Value { - #[inline(always)] - fn from(val: u128) -> Self { - ValueRepr::U128(val).into() - } -} - impl<'a> From> for Value { fn from(val: Key) -> Self { match val { @@ -257,19 +243,40 @@ impl<'a> From> for Value { } } +impl> FromIterator for Value { + fn from_iter>(iter: T) -> Self { + let vec = iter.into_iter().map(|v| v.into()).collect(); + + ValueRepr::Seq(Arc::new(vec)).into() + } +} + +impl, V: Into> FromIterator<(K, V)> for Value { + fn from_iter>(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, V: Into> From> for Value { fn from(val: BTreeMap) -> 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> From> for Value { fn from(val: Vec) -> Self { - ValueRepr::Seq(Arc::new(val.into_iter().map(|x| x.into()).collect())).into() + val.into_iter().map(|v| v.into()).collect() + } +} + +impl From> for Value { + fn from(object: Arc) -> Self { + Value::from(object as Arc) } } @@ -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>, Bytes); +value_from!(Arc>, Seq); +value_from!(Arc, Dynamic); fn unsupported_conversion(kind: ValueKind, target: &str) -> Error { Error::new( diff --git a/minijinja/src/value/mod.rs b/minijinja/src/value/mod.rs index 20f1fe1a..82b07966 100644 --- a/minijinja/src/value/mod.rs +++ b/minijinja/src/value/mod.rs @@ -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: @@ -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>` 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); +//! ``` // this module is based on the content module in insta which in turn is based // on the content module in serde::private::ser. @@ -422,17 +455,8 @@ impl Value { /// /// let val = Value::from_object(Thing { id: 42 }); /// ``` - pub fn from_object(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(value: Arc) -> Value { - ValueRepr::Dynamic(value as Arc).into() + pub fn from_object(value: T) -> Value { + Value::from(Arc::new(value) as Arc) } /// Creates a callable value from a function. @@ -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); diff --git a/minijinja/src/vm/context.rs b/minijinja/src/vm/context.rs index b866e959..ab1f0ca7 100644 --- a/minijinja/src/vm/context.rs +++ b/minijinja/src/vm/context.rs @@ -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())); } }