From 866eb0cb3758a919919009a421c92cd8a148906d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 1 Oct 2018 17:33:56 -0600 Subject: [PATCH 1/9] fix(context): Reduce scope of public API BREAKING CHANGE: `Context::get_val` is no longer public. Use `Context::get_val_by_index` instead. --- liquid-interpreter/src/context.rs | 19 +++++++++---------- src/tags/assign_tag.rs | 9 +++++---- src/tags/capture_block.rs | 5 +++-- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/liquid-interpreter/src/context.rs b/liquid-interpreter/src/context.rs index 792fe12ab..fe3d87d93 100644 --- a/liquid-interpreter/src/context.rs +++ b/liquid-interpreter/src/context.rs @@ -140,16 +140,6 @@ impl<'g> Stack<'g> { }; } - /// Gets a value from the rendering context. - pub fn get_val<'a>(&'a self, name: &str) -> Option<&'a Value> { - for frame in self.stack.iter().rev() { - if let rval @ Some(_) = frame.get(name) { - return rval; - } - } - self.globals.and_then(|g| g.get(name)) - } - /// Recursively index into the stack. pub fn get_val_by_index<'i, I: Iterator>( &self, @@ -174,6 +164,15 @@ impl<'g> Stack<'g> { }) } + fn get_val<'a>(&'a self, name: &str) -> Option<&'a Value> { + for frame in self.stack.iter().rev() { + if let rval @ Some(_) = frame.get(name) { + return rval; + } + } + self.globals.and_then(|g| g.get(name)) + } + /// Sets a value in the global context. pub fn set_global_val(&mut self, name: S, val: Value) -> Option where diff --git a/src/tags/assign_tag.rs b/src/tags/assign_tag.rs index f0af8f954..a41039eb5 100644 --- a/src/tags/assign_tag.rs +++ b/src/tags/assign_tag.rs @@ -57,6 +57,7 @@ mod test { use interpreter; use tags; use value::Value; + use value::Index; fn options() -> LiquidOptions { let mut options = LiquidOptions::default(); @@ -103,8 +104,8 @@ mod test { let output = template.render(&mut context).unwrap(); assert_eq!( - context.stack().get_val("freestyle"), - Some(&Value::scalar(false)) + context.stack().get_val_by_index([Index::with_key("freestyle")].iter()).unwrap(), + &Value::scalar(false) ); assert_eq!(output, ""); } @@ -124,8 +125,8 @@ mod test { let output = template.render(&mut context).unwrap(); assert_eq!( - context.stack().get_val("freestyle"), - Some(&Value::scalar(true)) + context.stack().get_val_by_index([Index::with_key("freestyle")].iter()).unwrap(), + &Value::scalar(true) ); assert_eq!(output, "

Freestyle!

"); } diff --git a/src/tags/capture_block.rs b/src/tags/capture_block.rs index 5d0fdd0ba..46a55ad18 100644 --- a/src/tags/capture_block.rs +++ b/src/tags/capture_block.rs @@ -66,6 +66,7 @@ mod test { use super::*; use compiler; use interpreter; + use value::Index; fn options() -> LiquidOptions { let mut options = LiquidOptions::default(); @@ -95,8 +96,8 @@ mod test { let output = template.render(&mut ctx).unwrap(); assert_eq!( - ctx.stack().get_val("attribute_name"), - Some(&Value::scalar("potato-42-color")) + ctx.stack().get_val_by_index([Index::with_key("attribute_name")].iter()).unwrap(), + &Value::scalar("potato-42-color") ); assert_eq!(output, ""); } From 61ae6de625c53171327263bafec954f0ee2a4977 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 2 Oct 2018 16:57:09 -0600 Subject: [PATCH 2/9] feat(value): Create to_value `to_value` lets any `impl Serialize` convert to a liquid_value::Value. This can be useful for creating the globals needed for the parser. --- liquid-value/Cargo.toml | 2 + liquid-value/src/lib.rs | 9 + liquid-value/src/ser.rs | 643 ++++++++++++++++++++++++++++++++++++ liquid-value/src/values.rs | 6 +- liquid-value/tests/value.rs | 3 +- 5 files changed, 660 insertions(+), 3 deletions(-) create mode 100644 liquid-value/src/ser.rs diff --git a/liquid-value/Cargo.toml b/liquid-value/Cargo.toml index 90ac39241..f169473f3 100644 --- a/liquid-value/Cargo.toml +++ b/liquid-value/Cargo.toml @@ -15,9 +15,11 @@ travis-ci = { repository = "cobalt-org/liquid-rust" } appveyor = { repository = "johannhof/liquid-rust" } [dependencies] +num-traits = "0.2" # Exposed in API chrono = "0.4" serde = { version = "1.0", features = ["derive"] } +liquid-error = { version = "0.15.0", path = "../liquid-error" } [dev-dependencies] difference = "2.0" diff --git a/liquid-value/src/lib.rs b/liquid-value/src/lib.rs index 91d693cac..682c4c160 100644 --- a/liquid-value/src/lib.rs +++ b/liquid-value/src/lib.rs @@ -7,11 +7,20 @@ #[macro_use] extern crate serde; extern crate chrono; +extern crate liquid_error; +extern crate num_traits; mod index; mod scalar; +mod ser; mod values; +/// Liquid Processing Errors. +pub mod error { + pub use liquid_error::*; +} + pub use index::*; pub use scalar::*; +pub use ser::*; pub use values::*; diff --git a/liquid-value/src/ser.rs b/liquid-value/src/ser.rs new file mode 100644 index 000000000..178307708 --- /dev/null +++ b/liquid-value/src/ser.rs @@ -0,0 +1,643 @@ +use std::fmt; + +use num_traits; +use serde::ser::Impossible; +use serde::{self, Serialize}; + +use error; +use values::Object; +use values::Value; + +/// Convert a `T` into `liquid_value:Value`. +/// +/// # Examples +/// +/// ```rust +/// let s = "foo"; +/// let value = liquid_value::to_value(s).unwrap(); +/// assert_eq!(value, liquid_value::Value::scalar(s)); +/// ``` +pub fn to_value(value: T) -> Result +where + T: Serialize, +{ + value.serialize(Serializer).map_err(|e| e.0) +} + +#[derive(Clone, Debug)] +struct SerError(error::Error); + +impl fmt::Display for SerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "{}", self.0) + } +} + +impl ::std::error::Error for SerError { + fn description(&self) -> &str { + self.0.description() + } + + fn cause(&self) -> Option<&::std::error::Error> { + ::std::error::Error::cause(&self.0) + } +} + +impl serde::ser::Error for SerError { + fn custom(msg: T) -> Self + where + T: fmt::Display, + { + SerError(error::Error::with_msg(format!("{}", msg))) + } +} + +struct Serializer; + +impl Serializer { + #[inline] + fn serialize_as_i32( + self, + value: T, + ) -> Result { + let value = num_traits::cast::cast::(value) + .ok_or_else(|| SerError(error::Error::with_msg("Cannot fit number")))?; + Ok(Value::scalar(value)) + } +} + +impl serde::Serializer for Serializer { + type Ok = Value; + type Error = SerError; + + type SerializeSeq = SerializeVec; + type SerializeTuple = SerializeVec; + type SerializeTupleStruct = SerializeVec; + type SerializeTupleVariant = SerializeTupleVariant; + type SerializeMap = SerializeMap; + type SerializeStruct = SerializeMap; + type SerializeStructVariant = SerializeStructVariant; + + #[inline] + fn serialize_bool(self, value: bool) -> Result { + Ok(Value::scalar(value)) + } + + #[inline] + fn serialize_i8(self, value: i8) -> Result { + self.serialize_as_i32(value) + } + + #[inline] + fn serialize_i16(self, value: i16) -> Result { + self.serialize_as_i32(value) + } + + #[inline] + fn serialize_i32(self, value: i32) -> Result { + Ok(Value::scalar(value)) + } + + fn serialize_i64(self, value: i64) -> Result { + self.serialize_as_i32(value) + } + + #[inline] + fn serialize_u8(self, value: u8) -> Result { + self.serialize_as_i32(value) + } + + #[inline] + fn serialize_u16(self, value: u16) -> Result { + self.serialize_as_i32(value) + } + + #[inline] + fn serialize_u32(self, value: u32) -> Result { + self.serialize_as_i32(value) + } + + #[inline] + fn serialize_u64(self, value: u64) -> Result { + self.serialize_as_i32(value) + } + + #[inline] + fn serialize_f32(self, value: f32) -> Result { + self.serialize_f64(value as f64) + } + + #[inline] + fn serialize_f64(self, value: f64) -> Result { + Ok(Value::scalar(value)) + } + + #[inline] + fn serialize_char(self, value: char) -> Result { + let mut s = String::new(); + s.push(value); + self.serialize_str(&s) + } + + #[inline] + fn serialize_str(self, value: &str) -> Result { + Ok(Value::scalar(value.to_owned())) + } + + fn serialize_bytes(self, value: &[u8]) -> Result { + let vec = value.iter().map(|&b| Value::scalar(b as i32)).collect(); + Ok(Value::Array(vec)) + } + + #[inline] + fn serialize_unit(self) -> Result { + Ok(Value::Nil) + } + + #[inline] + fn serialize_unit_struct(self, _name: &'static str) -> Result { + self.serialize_unit() + } + + #[inline] + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + self.serialize_str(variant) + } + + #[inline] + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: Serialize, + { + value.serialize(Serializer) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: Serialize, + { + let mut values = Object::new(); + values.insert(String::from(variant).into(), value.serialize(Serializer)?); + Ok(Value::Object(values)) + } + + #[inline] + fn serialize_none(self) -> Result { + self.serialize_unit() + } + + #[inline] + fn serialize_some(self, value: &T) -> Result + where + T: Serialize, + { + value.serialize(Serializer) + } + + fn serialize_seq(self, len: Option) -> Result { + Ok(SerializeVec { + vec: Vec::with_capacity(len.unwrap_or(0)), + }) + } + + fn serialize_tuple(self, len: usize) -> Result { + self.serialize_seq(Some(len)) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + Ok(SerializeVec { + vec: Vec::with_capacity(len), + }) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + Ok(SerializeTupleVariant { + name: String::from(variant), + vec: Vec::with_capacity(len), + }) + } + + fn serialize_map(self, _len: Option) -> Result { + Ok(SerializeMap::Map { + map: Object::new(), + next_key: None, + }) + } + + fn serialize_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + self.serialize_map(Some(len)) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + Ok(SerializeStructVariant { + name: String::from(variant), + map: Object::new(), + }) + } +} + +struct SerializeVec { + vec: Vec, +} + +struct SerializeTupleVariant { + name: String, + vec: Vec, +} + +enum SerializeMap { + Map { + map: Object, + next_key: Option, + }, +} + +struct SerializeStructVariant { + name: String, + map: Object, +} + +impl serde::ser::SerializeSeq for SerializeVec { + type Ok = Value; + type Error = SerError; + + fn serialize_element(&mut self, value: &T) -> Result<(), SerError> + where + T: Serialize, + { + self.vec.push(value.serialize(Serializer)?); + Ok(()) + } + + fn end(self) -> Result { + Ok(Value::Array(self.vec)) + } +} + +impl serde::ser::SerializeTuple for SerializeVec { + type Ok = Value; + type Error = SerError; + + fn serialize_element(&mut self, value: &T) -> Result<(), SerError> + where + T: Serialize, + { + serde::ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + serde::ser::SerializeSeq::end(self) + } +} + +impl serde::ser::SerializeTupleStruct for SerializeVec { + type Ok = Value; + type Error = SerError; + + fn serialize_field(&mut self, value: &T) -> Result<(), SerError> + where + T: Serialize, + { + serde::ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + serde::ser::SerializeSeq::end(self) + } +} + +impl serde::ser::SerializeTupleVariant for SerializeTupleVariant { + type Ok = Value; + type Error = SerError; + + fn serialize_field(&mut self, value: &T) -> Result<(), SerError> + where + T: Serialize, + { + self.vec.push(value.serialize(Serializer)?); + Ok(()) + } + + fn end(self) -> Result { + let mut object = Object::new(); + + object.insert(self.name.into(), Value::Array(self.vec)); + + Ok(Value::Object(object)) + } +} + +impl serde::ser::SerializeMap for SerializeMap { + type Ok = Value; + type Error = SerError; + + fn serialize_key(&mut self, key: &T) -> Result<(), SerError> + where + T: Serialize, + { + match *self { + SerializeMap::Map { + ref mut next_key, .. + } => { + *next_key = Some(try!(key.serialize(MapKeySerializer))); + Ok(()) + } + } + } + + fn serialize_value(&mut self, value: &T) -> Result<(), SerError> + where + T: Serialize, + { + match *self { + SerializeMap::Map { + ref mut map, + ref mut next_key, + } => { + let key = next_key.take(); + // Panic because this indicates a bug in the program rather than an + // expected failure. + let key = key.expect("serialize_value called before serialize_key"); + map.insert(key.into(), value.serialize(Serializer)?); + Ok(()) + } + } + } + + fn end(self) -> Result { + match self { + SerializeMap::Map { map, .. } => Ok(Value::Object(map)), + } + } +} + +struct MapKeySerializer; + +fn key_must_be_a_string() -> SerError { + SerError(error::Error::with_msg("Key must be a string.")) +} + +impl serde::Serializer for MapKeySerializer { + type Ok = String; + type Error = SerError; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + #[inline] + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + Ok(variant.to_owned()) + } + + #[inline] + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: Serialize, + { + value.serialize(self) + } + + fn serialize_bool(self, _value: bool) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_i8(self, value: i8) -> Result { + Ok(value.to_string()) + } + + fn serialize_i16(self, value: i16) -> Result { + Ok(value.to_string()) + } + + fn serialize_i32(self, value: i32) -> Result { + Ok(value.to_string()) + } + + fn serialize_i64(self, value: i64) -> Result { + Ok(value.to_string()) + } + + fn serialize_u8(self, value: u8) -> Result { + Ok(value.to_string()) + } + + fn serialize_u16(self, value: u16) -> Result { + Ok(value.to_string()) + } + + fn serialize_u32(self, value: u32) -> Result { + Ok(value.to_string()) + } + + fn serialize_u64(self, value: u64) -> Result { + Ok(value.to_string()) + } + + fn serialize_f32(self, _value: f32) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_f64(self, _value: f64) -> Result { + Err(key_must_be_a_string()) + } + + #[inline] + fn serialize_char(self, value: char) -> Result { + Ok({ + let mut s = String::new(); + s.push(value); + s + }) + } + + #[inline] + fn serialize_str(self, value: &str) -> Result { + Ok(value.to_owned()) + } + + fn serialize_bytes(self, _value: &[u8]) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_unit(self) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result + where + T: Serialize, + { + Err(key_must_be_a_string()) + } + + fn serialize_none(self) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_some(self, _value: &T) -> Result + where + T: Serialize, + { + Err(key_must_be_a_string()) + } + + fn serialize_seq(self, _len: Option) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_map(self, _len: Option) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } +} + +impl serde::ser::SerializeStruct for SerializeMap { + type Ok = Value; + type Error = SerError; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<(), SerError> + where + T: Serialize, + { + match *self { + SerializeMap::Map { .. } => { + try!(serde::ser::SerializeMap::serialize_key(self, key)); + serde::ser::SerializeMap::serialize_value(self, value) + } + } + } + + fn end(self) -> Result { + match self { + SerializeMap::Map { .. } => serde::ser::SerializeMap::end(self), + } + } +} + +impl serde::ser::SerializeStructVariant for SerializeStructVariant { + type Ok = Value; + type Error = SerError; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<(), SerError> + where + T: Serialize, + { + self.map + .insert(String::from(key).into(), value.serialize(Serializer)?); + Ok(()) + } + + fn end(self) -> Result { + let mut object = Object::new(); + + object.insert(self.name.into(), Value::Object(self.map)); + + Ok(Value::Object(object)) + } +} diff --git a/liquid-value/src/values.rs b/liquid-value/src/values.rs index 5abfd1e7a..617ef59c4 100644 --- a/liquid-value/src/values.rs +++ b/liquid-value/src/values.rs @@ -307,7 +307,8 @@ mod test { let a: Object = [ ("alpha".into(), Value::scalar("1")), ("beta".into(), Value::scalar(2f64)), - ].into_iter() + ] + .into_iter() .cloned() .collect(); let a = Value::Object(a); @@ -316,7 +317,8 @@ mod test { ("alpha".into(), Value::scalar("1")), ("beta".into(), Value::scalar(2f64)), ("gamma".into(), Value::Array(vec![])), - ].into_iter() + ] + .into_iter() .cloned() .collect(); let b = Value::Object(b); diff --git a/liquid-value/tests/value.rs b/liquid-value/tests/value.rs index 5cb1e5bf3..5f23e42df 100644 --- a/liquid-value/tests/value.rs +++ b/liquid-value/tests/value.rs @@ -149,7 +149,8 @@ pub fn deserialize_object() { ("Num".into(), liquid_value::Value::scalar(1f64)), ("Bool".into(), liquid_value::Value::scalar(true)), ("Str".into(), liquid_value::Value::scalar("true")), - ].iter() + ] + .iter() .cloned() .collect(); let expected = liquid_value::Value::Object(expected); From 5216cadb50cf86a84cac0e929acda99ad087caea Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 2 Oct 2018 21:09:18 -0600 Subject: [PATCH 3/9] chore: Style --- liquid-interpreter/src/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liquid-interpreter/src/context.rs b/liquid-interpreter/src/context.rs index fe3d87d93..6981b1aef 100644 --- a/liquid-interpreter/src/context.rs +++ b/liquid-interpreter/src/context.rs @@ -1,8 +1,8 @@ +use std::borrow; use std::collections::HashMap; use std::sync; use error::{Error, Result}; -use std::borrow; use value::{Index, Object, Value}; use super::Argument; From fde1397b5d81b015e30379432c8c017acb63c3b3 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 2 Oct 2018 21:28:30 -0600 Subject: [PATCH 4/9] feat(values): Return error from Globals --- liquid-interpreter/src/context.rs | 16 ++++++++-------- liquid-interpreter/src/globals.rs | 6 ++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/liquid-interpreter/src/context.rs b/liquid-interpreter/src/context.rs index 6981b1aef..9db04a3fd 100644 --- a/liquid-interpreter/src/context.rs +++ b/liquid-interpreter/src/context.rs @@ -151,9 +151,7 @@ impl<'g> Stack<'g> { let key = key.as_key().ok_or_else(|| { Error::with_msg("Root index must be an object key").context("index", format!("{}", key)) })?; - let value = self - .get_val(key) - .ok_or_else(|| Error::with_msg("Invalid index").context("index", key.to_owned()))?; + let value = self.get_val(key)?; indexes.fold(Ok(value), |value, index| { let value = value?; @@ -164,13 +162,15 @@ impl<'g> Stack<'g> { }) } - fn get_val<'a>(&'a self, name: &str) -> Option<&'a Value> { + fn get_val<'a>(&'a self, name: &str) -> Result<&'a Value> { for frame in self.stack.iter().rev() { - if let rval @ Some(_) = frame.get(name) { - return rval; + if let Some(rval) = frame.get(name) { + return Ok(rval); } } - self.globals.and_then(|g| g.get(name)) + self.globals + .ok_or_else(|| Error::with_msg("Invalid index").context("index", name.to_owned())) + .and_then(|g| g.get(name)) } /// Sets a value in the global context. @@ -361,7 +361,7 @@ mod test { let mut post = Object::new(); post.insert("number".into(), Value::scalar(42f64)); ctx.stack_mut().set_global_val("post", Value::Object(post)); - assert!(ctx.stack().get_val("post.number").is_none()); + assert!(ctx.stack().get_val("post.number").is_err()); } #[test] diff --git a/liquid-interpreter/src/globals.rs b/liquid-interpreter/src/globals.rs index bb370fc39..cdcc4dbe3 100644 --- a/liquid-interpreter/src/globals.rs +++ b/liquid-interpreter/src/globals.rs @@ -1,16 +1,18 @@ use std::fmt; +use error::{Error, Result}; use value::Object; use value::Value; /// Immutable view into a template's global variables. pub trait Globals: fmt::Debug { /// Access a global variable. - fn get<'a>(&'a self, name: &str) -> Option<&'a Value>; + fn get<'a>(&'a self, name: &str) -> Result<&'a Value>; } impl Globals for Object { - fn get<'a>(&'a self, name: &str) -> Option<&'a Value> { + fn get<'a>(&'a self, name: &str) -> Result<&'a Value> { self.get(name) + .ok_or_else(|| Error::with_msg("Invalid index").context("index", name.to_owned())) } } From a936ba5290130d1fe53ccb4b59c2d04744406674 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 3 Oct 2018 18:05:43 -0600 Subject: [PATCH 5/9] feat(interpreter): Create dedicated Path for indexing into a Value --- liquid-compiler/src/token.rs | 2 +- liquid-interpreter/src/argument.rs | 2 +- liquid-interpreter/src/context.rs | 53 ++++++++++---------- liquid-interpreter/src/variable.rs | 21 ++++---- liquid-value/Cargo.toml | 1 + liquid-value/src/lib.rs | 3 ++ liquid-value/src/path.rs | 78 ++++++++++++++++++++++++++++++ src/tags/assign_tag.rs | 10 ++-- src/tags/capture_block.rs | 8 +-- src/tags/case_block.rs | 10 ++-- src/tags/cycle_tag.rs | 6 +-- src/tags/for_block.rs | 20 ++++---- src/tags/if_block.rs | 34 ++++++------- src/tags/include_tag.rs | 8 +-- 14 files changed, 169 insertions(+), 87 deletions(-) create mode 100644 liquid-value/src/path.rs diff --git a/liquid-compiler/src/token.rs b/liquid-compiler/src/token.rs index 9c6bc0906..3052fee15 100644 --- a/liquid-compiler/src/token.rs +++ b/liquid-compiler/src/token.rs @@ -180,7 +180,7 @@ mod test { #[test] fn evaluate_handles_identifiers() { let mut ctx = Context::new(); - ctx.stack_mut().set_global_val("var0", Value::scalar(42f64)); + ctx.stack_mut().set_global("var0", Value::scalar(42f64)); assert_eq!( Token::Identifier("var0".to_owned()) .to_arg() diff --git a/liquid-interpreter/src/argument.rs b/liquid-interpreter/src/argument.rs index 93b1a7bc8..f22067cc8 100644 --- a/liquid-interpreter/src/argument.rs +++ b/liquid-interpreter/src/argument.rs @@ -22,7 +22,7 @@ impl Argument { Argument::Val(ref x) => x.clone(), Argument::Var(ref x) => context .stack() - .get_val_by_index(x.indexes().iter())? + .get(x.path())? .clone(), }; Ok(val) diff --git a/liquid-interpreter/src/context.rs b/liquid-interpreter/src/context.rs index 9db04a3fd..53ce3b00a 100644 --- a/liquid-interpreter/src/context.rs +++ b/liquid-interpreter/src/context.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::sync; use error::{Error, Result}; -use value::{Index, Object, Value}; +use value::{Object, Value, Path}; use super::Argument; use super::Globals; @@ -141,17 +141,18 @@ impl<'g> Stack<'g> { } /// Recursively index into the stack. - pub fn get_val_by_index<'i, I: Iterator>( + pub fn get( &self, - mut indexes: I, + path: &Path, ) -> Result<&Value> { + let mut indexes = path.iter(); let key = indexes .next() .ok_or_else(|| Error::with_msg("No index provided"))?; let key = key.as_key().ok_or_else(|| { Error::with_msg("Root index must be an object key").context("index", format!("{}", key)) })?; - let value = self.get_val(key)?; + let value = self.get_root(key)?; indexes.fold(Ok(value), |value, index| { let value = value?; @@ -162,7 +163,7 @@ impl<'g> Stack<'g> { }) } - fn get_val<'a>(&'a self, name: &str) -> Result<&'a Value> { + fn get_root<'a>(&'a self, name: &str) -> Result<&'a Value> { for frame in self.stack.iter().rev() { if let Some(rval) = frame.get(name) { return Ok(rval); @@ -174,7 +175,7 @@ impl<'g> Stack<'g> { } /// Sets a value in the global context. - pub fn set_global_val(&mut self, name: S, val: Value) -> Option + pub fn set_global(&mut self, name: S, val: Value) -> Option where S: Into>, { @@ -189,7 +190,7 @@ impl<'g> Stack<'g> { /// Panics if there is no frame on the local values stack. Context /// instances are created with a top-level stack frame in place, so /// this should never happen in a well-formed program. - pub fn set_val(&mut self, name: S, val: Value) -> Option + pub fn set(&mut self, name: S, val: Value) -> Option where S: Into>, { @@ -344,35 +345,37 @@ impl<'g> Context<'g> { mod test { use super::*; + use value::Index; + #[test] - fn get_val() { + fn stack_get_root() { let mut ctx = Context::new(); ctx.stack_mut() - .set_global_val("number", Value::scalar(42f64)); + .set_global("number", Value::scalar(42f64)); assert_eq!( - ctx.stack().get_val("number").unwrap(), + ctx.stack().get_root("number").unwrap(), &Value::scalar(42f64) ); } #[test] - fn get_val_failure() { + fn stack_get_root_failure() { let mut ctx = Context::new(); let mut post = Object::new(); post.insert("number".into(), Value::scalar(42f64)); - ctx.stack_mut().set_global_val("post", Value::Object(post)); - assert!(ctx.stack().get_val("post.number").is_err()); + ctx.stack_mut().set_global("post", Value::Object(post)); + assert!(ctx.stack().get_root("post.number").is_err()); } #[test] - fn get_val_by_index() { + fn stack_get() { let mut ctx = Context::new(); let mut post = Object::new(); post.insert("number".into(), Value::scalar(42f64)); - ctx.stack_mut().set_global_val("post", Value::Object(post)); - let indexes = vec![Index::with_key("post"), Index::with_key("number")]; + ctx.stack_mut().set_global("post", Value::Object(post)); + let indexes = vec![Index::with_key("post"), Index::with_key("number")].into_iter().collect(); assert_eq!( - ctx.stack().get_val_by_index(indexes.iter()).unwrap(), + ctx.stack().get(&indexes).unwrap(), &Value::scalar(42f64) ); } @@ -380,35 +383,35 @@ mod test { #[test] fn scoped_variables() { let mut ctx = Context::new(); - ctx.stack_mut().set_global_val("test", Value::scalar(42f64)); - assert_eq!(ctx.stack().get_val("test").unwrap(), &Value::scalar(42f64)); + ctx.stack_mut().set_global("test", Value::scalar(42f64)); + assert_eq!(ctx.stack().get_root("test").unwrap(), &Value::scalar(42f64)); ctx.run_in_scope(|new_scope| { // assert that values are chained to the parent scope assert_eq!( - new_scope.stack().get_val("test").unwrap(), + new_scope.stack().get_root("test").unwrap(), &Value::scalar(42f64) ); // set a new local value, and assert that it overrides the previous value new_scope .stack_mut() - .set_val("test", Value::scalar(3.14f64)); + .set("test", Value::scalar(3.14f64)); assert_eq!( - new_scope.stack().get_val("test").unwrap(), + new_scope.stack().get_root("test").unwrap(), &Value::scalar(3.14f64) ); // sat a new val that we will pick up outside the scope new_scope .stack_mut() - .set_global_val("global", Value::scalar("some value")); + .set_global("global", Value::scalar("some value")); }); // assert that the value has reverted to the old one - assert_eq!(ctx.stack().get_val("test").unwrap(), &Value::scalar(42f64)); + assert_eq!(ctx.stack().get_root("test").unwrap(), &Value::scalar(42f64)); assert_eq!( - ctx.stack().get_val("global").unwrap(), + ctx.stack().get_root("global").unwrap(), &Value::scalar("some value") ); } diff --git a/liquid-interpreter/src/variable.rs b/liquid-interpreter/src/variable.rs index e519d2ad9..1c0bed083 100644 --- a/liquid-interpreter/src/variable.rs +++ b/liquid-interpreter/src/variable.rs @@ -1,10 +1,9 @@ use std::fmt; use std::io::Write; -use itertools; - use error::{Result, ResultLiquidChainExt}; use value::Index; +use value::Path; use super::Context; use super::Renderable; @@ -12,38 +11,36 @@ use super::Renderable; /// A `Value` reference. #[derive(Clone, Debug, Default, PartialEq)] pub struct Variable { - indexes: Vec, + path: Path, } impl Variable { /// Create a `Value` reference. pub fn new>(value: I) -> Self { - let indexes = vec![value.into()]; - Self { indexes } + let path = Path::with_index(value); + Self { path } } - /// Access the `Value` reference. - pub fn indexes(&self) -> &[Index] { - &self.indexes + pub fn path(&self) -> &Path { + &self.path } } impl Extend for Variable { fn extend>(&mut self, iter: T) { - self.indexes.extend(iter); + self.path.extend(iter); } } impl fmt::Display for Variable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let data = itertools::join(self.indexes().iter(), "."); - write!(f, "{}", data) + write!(f, "{}", self.path) } } impl Renderable for Variable { fn render_to(&self, writer: &mut Write, context: &mut Context) -> Result<()> { - let value = context.stack().get_val_by_index(self.indexes.iter())?; + let value = context.stack().get(&self.path)?; write!(writer, "{}", value).chain("Failed to render")?; Ok(()) } diff --git a/liquid-value/Cargo.toml b/liquid-value/Cargo.toml index f169473f3..30233ce37 100644 --- a/liquid-value/Cargo.toml +++ b/liquid-value/Cargo.toml @@ -15,6 +15,7 @@ travis-ci = { repository = "cobalt-org/liquid-rust" } appveyor = { repository = "johannhof/liquid-rust" } [dependencies] +itertools = "0.7.0" num-traits = "0.2" # Exposed in API chrono = "0.4" diff --git a/liquid-value/src/lib.rs b/liquid-value/src/lib.rs index 682c4c160..fb0d56f86 100644 --- a/liquid-value/src/lib.rs +++ b/liquid-value/src/lib.rs @@ -7,6 +7,7 @@ #[macro_use] extern crate serde; extern crate chrono; +extern crate itertools; extern crate liquid_error; extern crate num_traits; @@ -14,6 +15,7 @@ mod index; mod scalar; mod ser; mod values; +mod path; /// Liquid Processing Errors. pub mod error { @@ -21,6 +23,7 @@ pub mod error { } pub use index::*; +pub use path::*; pub use scalar::*; pub use ser::*; pub use values::*; diff --git a/liquid-value/src/path.rs b/liquid-value/src/path.rs new file mode 100644 index 000000000..db0dfe5af --- /dev/null +++ b/liquid-value/src/path.rs @@ -0,0 +1,78 @@ +use std::fmt; +use std::iter; +use std::slice; + +use itertools; + +use super::Index; + +/// Path to a value in an `Object`. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Path { + indexes: Vec, +} + +impl Path { + /// Create a `Path` from iterator of `Index`s + pub fn new>(indexes: T) -> Self { + let indexes = indexes.into_iter().collect(); + Self { indexes } + } + + /// Create a `Value` reference. + pub fn with_index>(value: I) -> Self { + let indexes = vec![value.into()]; + Self { indexes } + } + + /// Access the `Value` reference. + pub fn iter(&self) -> IndexIter { + IndexIter(self.indexes.iter()) + } +} + +impl Extend for Path { + fn extend>(&mut self, iter: T) { + self.indexes.extend(iter); + } +} + +impl iter::FromIterator for Path { + fn from_iter(iter: I) -> Self + where + I: IntoIterator, + { + let indexes = iter.into_iter().collect(); + Self { indexes } + } +} + +impl fmt::Display for Path { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let data = itertools::join(self.iter(), "."); + write!(f, "{}", data) + } +} + +/// Iterate over indexes in a `Value`'s `Path`. +#[derive(Debug)] +pub struct IndexIter<'i>(slice::Iter<'i, Index>); + +impl<'i> Iterator for IndexIter<'i> { + type Item = &'i Index; + + #[inline] + fn next(&mut self) -> Option<&'i Index> { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + #[inline] + fn count(self) -> usize { + self.0.count() + } +} diff --git a/src/tags/assign_tag.rs b/src/tags/assign_tag.rs index a41039eb5..19b0860e8 100644 --- a/src/tags/assign_tag.rs +++ b/src/tags/assign_tag.rs @@ -27,7 +27,7 @@ impl Renderable for Assign { let value = self.src.evaluate(context).trace_with(|| self.trace())?; context .stack_mut() - .set_global_val(self.dst.to_owned(), value); + .set_global(self.dst.to_owned(), value); Ok(()) } } @@ -93,7 +93,7 @@ mod test { // test one: no matching value in `tags` { let mut context = Context::new(); - context.stack_mut().set_global_val( + context.stack_mut().set_global( "tags", Value::Array(vec![ Value::scalar("alpha"), @@ -104,7 +104,7 @@ mod test { let output = template.render(&mut context).unwrap(); assert_eq!( - context.stack().get_val_by_index([Index::with_key("freestyle")].iter()).unwrap(), + context.stack().get(&vec![Index::with_key("freestyle")].into_iter().collect()).unwrap(), &Value::scalar(false) ); assert_eq!(output, ""); @@ -113,7 +113,7 @@ mod test { // test two: matching value in `tags` { let mut context = Context::new(); - context.stack_mut().set_global_val( + context.stack_mut().set_global( "tags", Value::Array(vec![ Value::scalar("alpha"), @@ -125,7 +125,7 @@ mod test { let output = template.render(&mut context).unwrap(); assert_eq!( - context.stack().get_val_by_index([Index::with_key("freestyle")].iter()).unwrap(), + context.stack().get(&vec![Index::with_key("freestyle")].into_iter().collect()).unwrap(), &Value::scalar(true) ); assert_eq!(output, "

Freestyle!

"); diff --git a/src/tags/capture_block.rs b/src/tags/capture_block.rs index 46a55ad18..8c89bb48b 100644 --- a/src/tags/capture_block.rs +++ b/src/tags/capture_block.rs @@ -33,7 +33,7 @@ impl Renderable for Capture { let output = String::from_utf8(captured).expect("render only writes UTF-8"); context .stack_mut() - .set_global_val(self.id.to_owned(), Value::scalar(output)); + .set_global(self.id.to_owned(), Value::scalar(output)); Ok(()) } } @@ -91,12 +91,12 @@ mod test { let mut ctx = Context::new(); ctx.stack_mut() - .set_global_val("item", Value::scalar("potato")); - ctx.stack_mut().set_global_val("i", Value::scalar(42f64)); + .set_global("item", Value::scalar("potato")); + ctx.stack_mut().set_global("i", Value::scalar(42f64)); let output = template.render(&mut ctx).unwrap(); assert_eq!( - ctx.stack().get_val_by_index([Index::with_key("attribute_name")].iter()).unwrap(), + ctx.stack().get(&vec![Index::with_key("attribute_name")].into_iter().collect()).unwrap(), &Value::scalar("potato-42-color") ); assert_eq!(output, ""); diff --git a/src/tags/case_block.rs b/src/tags/case_block.rs index af9973804..0d4eb8841 100644 --- a/src/tags/case_block.rs +++ b/src/tags/case_block.rs @@ -210,18 +210,18 @@ mod test { .unwrap(); let mut context = Context::new(); - context.stack_mut().set_global_val("x", Value::scalar(2f64)); + context.stack_mut().set_global("x", Value::scalar(2f64)); assert_eq!(template.render(&mut context).unwrap(), "two"); - context.stack_mut().set_global_val("x", Value::scalar(3f64)); + context.stack_mut().set_global("x", Value::scalar(3f64)); assert_eq!(template.render(&mut context).unwrap(), "three and a half"); - context.stack_mut().set_global_val("x", Value::scalar(4f64)); + context.stack_mut().set_global("x", Value::scalar(4f64)); assert_eq!(template.render(&mut context).unwrap(), "three and a half"); context .stack_mut() - .set_global_val("x", Value::scalar("nope")); + .set_global("x", Value::scalar("nope")); assert_eq!(template.render(&mut context).unwrap(), "otherwise"); } @@ -244,7 +244,7 @@ mod test { let mut context = Context::new(); context .stack_mut() - .set_global_val("x", Value::scalar("nope")); + .set_global("x", Value::scalar("nope")); assert_eq!(template.render(&mut context).unwrap(), ""); } diff --git a/src/tags/cycle_tag.rs b/src/tags/cycle_tag.rs index 13399d499..b165c010d 100644 --- a/src/tags/cycle_tag.rs +++ b/src/tags/cycle_tag.rs @@ -178,13 +178,13 @@ mod test { let mut context = Context::new(); context .stack_mut() - .set_global_val("alpha", Value::scalar(1f64)); + .set_global("alpha", Value::scalar(1f64)); context .stack_mut() - .set_global_val("beta", Value::scalar(2f64)); + .set_global("beta", Value::scalar(2f64)); context .stack_mut() - .set_global_val("gamma", Value::scalar(3f64)); + .set_global("gamma", Value::scalar(3f64)); let output = template.render(&mut context); diff --git a/src/tags/for_block.rs b/src/tags/for_block.rs index 7eab2bb4a..41915db79 100644 --- a/src/tags/for_block.rs +++ b/src/tags/for_block.rs @@ -139,10 +139,10 @@ impl Renderable for For { scope .stack_mut() - .set_val("forloop", Value::Object(helper_vars.clone())); + .set("forloop", Value::Object(helper_vars.clone())); scope .stack_mut() - .set_val(self.var_name.to_owned(), v.clone()); + .set(self.var_name.to_owned(), v.clone()); self.item_template .render_to(writer, &mut scope) .trace_with(|| self.trace()) @@ -328,7 +328,7 @@ mod test { ).unwrap(); let mut context: Context = Default::default(); - context.stack_mut().set_global_val( + context.stack_mut().set_global( "array", Value::Array(vec![ Value::scalar(22f64), @@ -382,10 +382,10 @@ mod test { let mut context = Context::new(); context .stack_mut() - .set_global_val("alpha", Value::scalar(42i32)); + .set_global("alpha", Value::scalar(42i32)); context .stack_mut() - .set_global_val("omega", Value::scalar(46i32)); + .set_global("omega", Value::scalar(46i32)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "#1 test 42, #2 test 43, #3 test 44, #4 test 45, "); } @@ -438,13 +438,13 @@ mod test { .unwrap(); let mut context = Context::new(); - context.stack_mut().set_global_val("i", Value::scalar(0i32)); - context.stack_mut().set_global_val("j", Value::scalar(0i32)); + context.stack_mut().set_global("i", Value::scalar(0i32)); + context.stack_mut().set_global("j", Value::scalar(0i32)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "empty outer"); - context.stack_mut().set_global_val("i", Value::scalar(1i32)); - context.stack_mut().set_global_val("j", Value::scalar(0i32)); + context.stack_mut().set_global("i", Value::scalar(1i32)); + context.stack_mut().set_global("j", Value::scalar(0i32)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "empty inner"); } @@ -643,7 +643,7 @@ mod test { .set_filters(&sync::Arc::new(filters)) .build(); - context.stack_mut().set_global_val( + context.stack_mut().set_global( "array", Value::Array(vec![ Value::scalar("alpha"), diff --git a/src/tags/if_block.rs b/src/tags/if_block.rs index fc58718ab..f945e35fd 100644 --- a/src/tags/if_block.rs +++ b/src/tags/if_block.rs @@ -317,7 +317,7 @@ mod test { // Explicit nil let mut context = Context::new(); - context.stack_mut().set_global_val("truthy", Value::Nil); + context.stack_mut().set_global("truthy", Value::Nil); let output = template.render(&mut context).unwrap(); assert_eq!(output, "nope"); @@ -325,7 +325,7 @@ mod test { let mut context = Context::new(); context .stack_mut() - .set_global_val("truthy", Value::scalar(false)); + .set_global("truthy", Value::scalar(false)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "nope"); @@ -333,7 +333,7 @@ mod test { let mut context = Context::new(); context .stack_mut() - .set_global_val("truthy", Value::scalar(true)); + .set_global("truthy", Value::scalar(true)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "yep"); } @@ -354,7 +354,7 @@ mod test { let mut context = Context::new(); context .stack_mut() - .set_global_val("some_value", Value::scalar(1f64)); + .set_global("some_value", Value::scalar(1f64)); let output = template.render(&mut context).unwrap(); assert_eq!(output, ""); @@ -366,7 +366,7 @@ mod test { let mut context = Context::new(); context .stack_mut() - .set_global_val("some_value", Value::scalar(42f64)); + .set_global("some_value", Value::scalar(42f64)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "unless body"); } @@ -393,10 +393,10 @@ mod test { let mut context = Context::new(); context .stack_mut() - .set_global_val("truthy", Value::scalar(true)); + .set_global("truthy", Value::scalar(true)); context .stack_mut() - .set_global_val("also_truthy", Value::scalar(false)); + .set_global("also_truthy", Value::scalar(false)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "yep, not also truthy"); } @@ -421,7 +421,7 @@ mod test { .unwrap(); let mut context = Context::new(); - context.stack_mut().set_global_val("a", Value::scalar(1f64)); + context.stack_mut().set_global("a", Value::scalar(1f64)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "first"); @@ -431,7 +431,7 @@ mod test { .unwrap(); let mut context = Context::new(); - context.stack_mut().set_global_val("a", Value::scalar(2f64)); + context.stack_mut().set_global("a", Value::scalar(2f64)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "second"); @@ -441,7 +441,7 @@ mod test { .unwrap(); let mut context = Context::new(); - context.stack_mut().set_global_val("a", Value::scalar(3f64)); + context.stack_mut().set_global("a", Value::scalar(3f64)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "third"); @@ -453,7 +453,7 @@ mod test { let mut context = Context::new(); context .stack_mut() - .set_global_val("a", Value::scalar("else")); + .set_global("a", Value::scalar("else")); let output = template.render(&mut context).unwrap(); assert_eq!(output, "fourth"); } @@ -492,7 +492,7 @@ mod test { let mut context = Context::new(); context .stack_mut() - .set_global_val("movie", Value::scalar("Star Wars")); + .set_global("movie", Value::scalar("Star Wars")); let output = template.render(&mut context).unwrap(); assert_eq!(output, "if true"); @@ -505,7 +505,7 @@ mod test { let mut context = Context::new(); context .stack_mut() - .set_global_val("movie", Value::scalar("Batman")); + .set_global("movie", Value::scalar("Batman")); let output = template.render(&mut context).unwrap(); assert_eq!(output, "if false"); } @@ -523,7 +523,7 @@ mod test { obj.insert("Star Wars".into(), Value::scalar("1977")); context .stack_mut() - .set_global_val("movies", Value::Object(obj)); + .set_global("movies", Value::Object(obj)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "if true"); } @@ -540,7 +540,7 @@ mod test { let obj = Object::new(); context .stack_mut() - .set_global_val("movies", Value::Object(obj)); + .set_global("movies", Value::Object(obj)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "if false"); } @@ -561,7 +561,7 @@ mod test { ]; context .stack_mut() - .set_global_val("movies", Value::Array(arr)); + .set_global("movies", Value::Array(arr)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "if true"); } @@ -578,7 +578,7 @@ mod test { let arr = vec![Value::scalar("Alien")]; context .stack_mut() - .set_global_val("movies", Value::Array(arr)); + .set_global("movies", Value::Array(arr)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "if false"); } diff --git a/src/tags/include_tag.rs b/src/tags/include_tag.rs index 6f8359341..819a305b1 100644 --- a/src/tags/include_tag.rs +++ b/src/tags/include_tag.rs @@ -103,10 +103,10 @@ mod test { .build(); context .stack_mut() - .set_global_val("num", value::Value::scalar(5f64)); + .set_global("num", value::Value::scalar(5f64)); context .stack_mut() - .set_global_val("numTwo", value::Value::scalar(10f64)); + .set_global("numTwo", value::Value::scalar(10f64)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "5 wat wot\n"); } @@ -126,10 +126,10 @@ mod test { .build(); context .stack_mut() - .set_global_val("num", value::Value::scalar(5f64)); + .set_global("num", value::Value::scalar(5f64)); context .stack_mut() - .set_global_val("numTwo", value::Value::scalar(10f64)); + .set_global("numTwo", value::Value::scalar(10f64)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "5 wat wot\n"); } From 78db20609d61f7aecc3fad5cb6d4a48320f60f1d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 3 Oct 2018 18:10:48 -0600 Subject: [PATCH 6/9] chore: rustfmt --- liquid-compiler/src/lexer.rs | 4 +--- liquid-interpreter/src/argument.rs | 5 +---- liquid-interpreter/src/context.rs | 25 ++++++++-------------- liquid-value/src/lib.rs | 2 +- liquid-value/src/path.rs | 2 +- liquid-value/src/ser.rs | 19 ++++------------- src/bin/liquid-dbg/main.rs | 3 +-- src/filters/math.rs | 15 +++++--------- src/filters/mod.rs | 3 ++- src/parser.rs | 33 ++++++++++-------------------- src/tags/assign_tag.rs | 16 +++++++++------ src/tags/capture_block.rs | 10 ++++++--- src/tags/case_block.rs | 15 +++++--------- src/tags/cycle_tag.rs | 12 +++-------- src/tags/for_block.rs | 4 +--- src/tags/if_block.rs | 20 +++++------------- src/tags/include_tag.rs | 3 ++- tests/multithreading.rs | 3 +-- 18 files changed, 70 insertions(+), 124 deletions(-) diff --git a/liquid-compiler/src/lexer.rs b/liquid-compiler/src/lexer.rs index 68a7d7cf5..5b0fe825c 100644 --- a/liquid-compiler/src/lexer.rs +++ b/liquid-compiler/src/lexer.rs @@ -235,9 +235,7 @@ mod test { ); assert_eq!( split_atom("truc | filter:arg1,arg2"), - vec![ - "truc", " ", "", "|", "", " ", "filter", ":", "arg1", ",", "arg2", - ] + vec!["truc", " ", "", "|", "", " ", "filter", ":", "arg1", ",", "arg2",] ); } diff --git a/liquid-interpreter/src/argument.rs b/liquid-interpreter/src/argument.rs index f22067cc8..04a2095f3 100644 --- a/liquid-interpreter/src/argument.rs +++ b/liquid-interpreter/src/argument.rs @@ -20,10 +20,7 @@ impl Argument { pub fn evaluate(&self, context: &Context) -> Result { let val = match *self { Argument::Val(ref x) => x.clone(), - Argument::Var(ref x) => context - .stack() - .get(x.path())? - .clone(), + Argument::Var(ref x) => context.stack().get(x.path())?.clone(), }; Ok(val) } diff --git a/liquid-interpreter/src/context.rs b/liquid-interpreter/src/context.rs index 53ce3b00a..56a0130cc 100644 --- a/liquid-interpreter/src/context.rs +++ b/liquid-interpreter/src/context.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::sync; use error::{Error, Result}; -use value::{Object, Value, Path}; +use value::{Object, Path, Value}; use super::Argument; use super::Globals; @@ -89,7 +89,7 @@ impl<'a, 'g> CycleState<'a, 'g> { return Err(Error::with_msg( "cycle index out of bounds, most likely from mismatched cycles", ).context("index", format!("{}", index)) - .context("count", format!("{}", values.len()))); + .context("count", format!("{}", values.len()))); } let val = values[index].evaluate(self.context)?; @@ -141,10 +141,7 @@ impl<'g> Stack<'g> { } /// Recursively index into the stack. - pub fn get( - &self, - path: &Path, - ) -> Result<&Value> { + pub fn get(&self, path: &Path) -> Result<&Value> { let mut indexes = path.iter(); let key = indexes .next() @@ -350,8 +347,7 @@ mod test { #[test] fn stack_get_root() { let mut ctx = Context::new(); - ctx.stack_mut() - .set_global("number", Value::scalar(42f64)); + ctx.stack_mut().set_global("number", Value::scalar(42f64)); assert_eq!( ctx.stack().get_root("number").unwrap(), &Value::scalar(42f64) @@ -373,11 +369,10 @@ mod test { let mut post = Object::new(); post.insert("number".into(), Value::scalar(42f64)); ctx.stack_mut().set_global("post", Value::Object(post)); - let indexes = vec![Index::with_key("post"), Index::with_key("number")].into_iter().collect(); - assert_eq!( - ctx.stack().get(&indexes).unwrap(), - &Value::scalar(42f64) - ); + let indexes = vec![Index::with_key("post"), Index::with_key("number")] + .into_iter() + .collect(); + assert_eq!(ctx.stack().get(&indexes).unwrap(), &Value::scalar(42f64)); } #[test] @@ -394,9 +389,7 @@ mod test { ); // set a new local value, and assert that it overrides the previous value - new_scope - .stack_mut() - .set("test", Value::scalar(3.14f64)); + new_scope.stack_mut().set("test", Value::scalar(3.14f64)); assert_eq!( new_scope.stack().get_root("test").unwrap(), &Value::scalar(3.14f64) diff --git a/liquid-value/src/lib.rs b/liquid-value/src/lib.rs index fb0d56f86..d4e95a26a 100644 --- a/liquid-value/src/lib.rs +++ b/liquid-value/src/lib.rs @@ -12,10 +12,10 @@ extern crate liquid_error; extern crate num_traits; mod index; +mod path; mod scalar; mod ser; mod values; -mod path; /// Liquid Processing Errors. pub mod error { diff --git a/liquid-value/src/path.rs b/liquid-value/src/path.rs index db0dfe5af..0380cedcc 100644 --- a/liquid-value/src/path.rs +++ b/liquid-value/src/path.rs @@ -14,7 +14,7 @@ pub struct Path { impl Path { /// Create a `Path` from iterator of `Index`s - pub fn new>(indexes: T) -> Self { + pub fn new>(indexes: T) -> Self { let indexes = indexes.into_iter().collect(); Self { indexes } } diff --git a/liquid-value/src/ser.rs b/liquid-value/src/ser.rs index 178307708..e042b8c90 100644 --- a/liquid-value/src/ser.rs +++ b/liquid-value/src/ser.rs @@ -48,7 +48,7 @@ impl serde::ser::Error for SerError { where T: fmt::Display, { - SerError(error::Error::with_msg(format!("{}", msg))) + SerError(error::Error::with_msg(format!("{}", msg))) } } @@ -56,10 +56,7 @@ struct Serializer; impl Serializer { #[inline] - fn serialize_as_i32( - self, - value: T, - ) -> Result { + fn serialize_as_i32(self, value: T) -> Result { let value = num_traits::cast::cast::(value) .ok_or_else(|| SerError(error::Error::with_msg("Cannot fit number")))?; Ok(Value::scalar(value)) @@ -593,11 +590,7 @@ impl serde::ser::SerializeStruct for SerializeMap { type Ok = Value; type Error = SerError; - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<(), SerError> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), SerError> where T: Serialize, { @@ -620,11 +613,7 @@ impl serde::ser::SerializeStructVariant for SerializeStructVariant { type Ok = Value; type Error = SerError; - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<(), SerError> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), SerError> where T: Serialize, { diff --git a/src/bin/liquid-dbg/main.rs b/src/bin/liquid-dbg/main.rs index 2e7e7ce82..c489d104b 100644 --- a/src/bin/liquid-dbg/main.rs +++ b/src/bin/liquid-dbg/main.rs @@ -113,8 +113,7 @@ fn run() -> Result<()> { .map(|s| { let p = path::PathBuf::from(s); build_context(p.as_path()) - }) - .map_or(Ok(None), |r| r.map(Some))? + }).map_or(Ok(None), |r| r.map(Some))? .unwrap_or_else(liquid::value::Object::new); let output = template.render(&data)?; match matches.value_of("output") { diff --git a/src/filters/math.rs b/src/filters/math.rs index 6b77e4ba3..a1e4b498f 100644 --- a/src/filters/math.rs +++ b/src/filters/math.rs @@ -35,8 +35,7 @@ pub fn plus(input: &Value, args: &[Value]) -> FilterResult { input .to_float() .and_then(|i| operand.to_float().map(|o| Value::scalar(i + o))) - }) - .ok_or_else(|| invalid_argument(0, "Number expected"))?; + }).ok_or_else(|| invalid_argument(0, "Number expected"))?; Ok(result) } @@ -59,8 +58,7 @@ pub fn minus(input: &Value, args: &[Value]) -> FilterResult { input .to_float() .and_then(|i| operand.to_float().map(|o| Value::scalar(i - o))) - }) - .ok_or_else(|| invalid_argument(0, "Number expected"))?; + }).ok_or_else(|| invalid_argument(0, "Number expected"))?; Ok(result) } @@ -83,8 +81,7 @@ pub fn times(input: &Value, args: &[Value]) -> FilterResult { input .to_float() .and_then(|i| operand.to_float().map(|o| Value::scalar(i * o))) - }) - .ok_or_else(|| invalid_argument(0, "Number expected"))?; + }).ok_or_else(|| invalid_argument(0, "Number expected"))?; Ok(result) } @@ -107,8 +104,7 @@ pub fn divided_by(input: &Value, args: &[Value]) -> FilterResult { input .to_float() .and_then(|i| operand.to_float().map(|o| Value::scalar(i / o))) - }) - .ok_or_else(|| invalid_argument(0, "Number expected"))?; + }).ok_or_else(|| invalid_argument(0, "Number expected"))?; Ok(result) } @@ -131,8 +127,7 @@ pub fn modulo(input: &Value, args: &[Value]) -> FilterResult { input .to_float() .and_then(|i| operand.to_float().map(|o| Value::scalar(i % o))) - }) - .ok_or_else(|| invalid_argument(0, "Number expected"))?; + }).ok_or_else(|| invalid_argument(0, "Number expected"))?; Ok(result) } diff --git a/src/filters/mod.rs b/src/filters/mod.rs index c3a72e375..f28f957ad 100644 --- a/src/filters/mod.rs +++ b/src/filters/mod.rs @@ -221,7 +221,8 @@ pub fn truncate(input: &Value, args: &[Value]) -> FilterResult { .take(l) .collect::>() .join("") - .to_string() + truncate_string.as_ref(); + .to_string() + + truncate_string.as_ref(); Value::scalar(result) } else { input.clone() diff --git a/src/parser.rs b/src/parser.rs index 65084b8c2..71d1a0215 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -61,8 +61,7 @@ impl ParserBuilder { .filter( "capitalize", filters::capitalize as interpreter::FnFilterValue, - ) - .filter("ceil", filters::ceil as interpreter::FnFilterValue) + ).filter("ceil", filters::ceil as interpreter::FnFilterValue) .filter("compact", filters::compact as interpreter::FnFilterValue) .filter("concat", filters::concat as interpreter::FnFilterValue) .filter("date", filters::date as interpreter::FnFilterValue) @@ -70,14 +69,12 @@ impl ParserBuilder { .filter( "divided_by", filters::divided_by as interpreter::FnFilterValue, - ) - .filter("downcase", filters::downcase as interpreter::FnFilterValue) + ).filter("downcase", filters::downcase as interpreter::FnFilterValue) .filter("escape", filters::escape as interpreter::FnFilterValue) .filter( "escape_once", filters::escape_once as interpreter::FnFilterValue, - ) - .filter("first", filters::first as interpreter::FnFilterValue) + ).filter("first", filters::first as interpreter::FnFilterValue) .filter("floor", filters::floor as interpreter::FnFilterValue) .filter("join", filters::join as interpreter::FnFilterValue) .filter("last", filters::last as interpreter::FnFilterValue) @@ -88,20 +85,17 @@ impl ParserBuilder { .filter( "newline_to_br", filters::newline_to_br as interpreter::FnFilterValue, - ) - .filter("plus", filters::plus as interpreter::FnFilterValue) + ).filter("plus", filters::plus as interpreter::FnFilterValue) .filter("prepend", filters::prepend as interpreter::FnFilterValue) .filter("remove", filters::remove as interpreter::FnFilterValue) .filter( "remove_first", filters::remove_first as interpreter::FnFilterValue, - ) - .filter("replace", filters::replace as interpreter::FnFilterValue) + ).filter("replace", filters::replace as interpreter::FnFilterValue) .filter( "replace_first", filters::replace_first as interpreter::FnFilterValue, - ) - .filter("reverse", filters::reverse as interpreter::FnFilterValue) + ).filter("reverse", filters::reverse as interpreter::FnFilterValue) .filter("round", filters::round as interpreter::FnFilterValue) .filter("rstrip", filters::rstrip as interpreter::FnFilterValue) .filter("size", filters::size as interpreter::FnFilterValue) @@ -110,30 +104,25 @@ impl ParserBuilder { .filter( "sort_natural", filters::sort_natural as interpreter::FnFilterValue, - ) - .filter("split", filters::split as interpreter::FnFilterValue) + ).filter("split", filters::split as interpreter::FnFilterValue) .filter("strip", filters::strip as interpreter::FnFilterValue) .filter( "strip_html", filters::strip_html as interpreter::FnFilterValue, - ) - .filter( + ).filter( "strip_newlines", filters::strip_newlines as interpreter::FnFilterValue, - ) - .filter("times", filters::times as interpreter::FnFilterValue) + ).filter("times", filters::times as interpreter::FnFilterValue) .filter("truncate", filters::truncate as interpreter::FnFilterValue) .filter( "truncatewords", filters::truncatewords as interpreter::FnFilterValue, - ) - .filter("uniq", filters::uniq as interpreter::FnFilterValue) + ).filter("uniq", filters::uniq as interpreter::FnFilterValue) .filter("upcase", filters::upcase as interpreter::FnFilterValue) .filter( "url_decode", filters::url_decode as interpreter::FnFilterValue, - ) - .filter( + ).filter( "url_encode", filters::url_encode as interpreter::FnFilterValue, ) diff --git a/src/tags/assign_tag.rs b/src/tags/assign_tag.rs index 19b0860e8..91a8764fc 100644 --- a/src/tags/assign_tag.rs +++ b/src/tags/assign_tag.rs @@ -25,9 +25,7 @@ impl Assign { impl Renderable for Assign { fn render_to(&self, _writer: &mut Write, context: &mut Context) -> Result<()> { let value = self.src.evaluate(context).trace_with(|| self.trace())?; - context - .stack_mut() - .set_global(self.dst.to_owned(), value); + context.stack_mut().set_global(self.dst.to_owned(), value); Ok(()) } } @@ -56,8 +54,8 @@ mod test { use compiler; use interpreter; use tags; - use value::Value; use value::Index; + use value::Value; fn options() -> LiquidOptions { let mut options = LiquidOptions::default(); @@ -104,7 +102,10 @@ mod test { let output = template.render(&mut context).unwrap(); assert_eq!( - context.stack().get(&vec![Index::with_key("freestyle")].into_iter().collect()).unwrap(), + context + .stack() + .get(&vec![Index::with_key("freestyle")].into_iter().collect()) + .unwrap(), &Value::scalar(false) ); assert_eq!(output, ""); @@ -125,7 +126,10 @@ mod test { let output = template.render(&mut context).unwrap(); assert_eq!( - context.stack().get(&vec![Index::with_key("freestyle")].into_iter().collect()).unwrap(), + context + .stack() + .get(&vec![Index::with_key("freestyle")].into_iter().collect()) + .unwrap(), &Value::scalar(true) ); assert_eq!(output, "

Freestyle!

"); diff --git a/src/tags/capture_block.rs b/src/tags/capture_block.rs index 8c89bb48b..889fa18f6 100644 --- a/src/tags/capture_block.rs +++ b/src/tags/capture_block.rs @@ -90,13 +90,17 @@ mod test { .unwrap(); let mut ctx = Context::new(); - ctx.stack_mut() - .set_global("item", Value::scalar("potato")); + ctx.stack_mut().set_global("item", Value::scalar("potato")); ctx.stack_mut().set_global("i", Value::scalar(42f64)); let output = template.render(&mut ctx).unwrap(); assert_eq!( - ctx.stack().get(&vec![Index::with_key("attribute_name")].into_iter().collect()).unwrap(), + ctx.stack() + .get( + &vec![Index::with_key("attribute_name")] + .into_iter() + .collect() + ).unwrap(), &Value::scalar("potato-42-color") ); assert_eq!(output, ""); diff --git a/src/tags/case_block.rs b/src/tags/case_block.rs index 0d4eb8841..a08ec0d90 100644 --- a/src/tags/case_block.rs +++ b/src/tags/case_block.rs @@ -125,10 +125,9 @@ fn parse_sections<'e>( match parse_condition(&children[0])? { Conditional::Cond(conds) => { - let template = - Template::new(parse(leading, options).trace_with(|| { - format!("{{% when {} %}}", itertools::join(conds.iter(), " or ")) - })?); + let template = Template::new(parse(leading, options).trace_with(|| { + format!("{{% when {} %}}", itertools::join(conds.iter(), " or ")) + })?); case.cases.push(CaseOption::new(conds, template)); } Conditional::Else => { @@ -219,9 +218,7 @@ mod test { context.stack_mut().set_global("x", Value::scalar(4f64)); assert_eq!(template.render(&mut context).unwrap(), "three and a half"); - context - .stack_mut() - .set_global("x", Value::scalar("nope")); + context.stack_mut().set_global("x", Value::scalar("nope")); assert_eq!(template.render(&mut context).unwrap(), "otherwise"); } @@ -242,9 +239,7 @@ mod test { .unwrap(); let mut context = Context::new(); - context - .stack_mut() - .set_global("x", Value::scalar("nope")); + context.stack_mut().set_global("x", Value::scalar("nope")); assert_eq!(template.render(&mut context).unwrap(), ""); } diff --git a/src/tags/cycle_tag.rs b/src/tags/cycle_tag.rs index b165c010d..f1d7d1eb9 100644 --- a/src/tags/cycle_tag.rs +++ b/src/tags/cycle_tag.rs @@ -176,15 +176,9 @@ mod test { .unwrap(); let mut context = Context::new(); - context - .stack_mut() - .set_global("alpha", Value::scalar(1f64)); - context - .stack_mut() - .set_global("beta", Value::scalar(2f64)); - context - .stack_mut() - .set_global("gamma", Value::scalar(3f64)); + context.stack_mut().set_global("alpha", Value::scalar(1f64)); + context.stack_mut().set_global("beta", Value::scalar(2f64)); + context.stack_mut().set_global("gamma", Value::scalar(3f64)); let output = template.render(&mut context); diff --git a/src/tags/for_block.rs b/src/tags/for_block.rs index 41915db79..88d74a8cf 100644 --- a/src/tags/for_block.rs +++ b/src/tags/for_block.rs @@ -140,9 +140,7 @@ impl Renderable for For { scope .stack_mut() .set("forloop", Value::Object(helper_vars.clone())); - scope - .stack_mut() - .set(self.var_name.to_owned(), v.clone()); + scope.stack_mut().set(self.var_name.to_owned(), v.clone()); self.item_template .render_to(writer, &mut scope) .trace_with(|| self.trace()) diff --git a/src/tags/if_block.rs b/src/tags/if_block.rs index f945e35fd..60d45f1fe 100644 --- a/src/tags/if_block.rs +++ b/src/tags/if_block.rs @@ -451,9 +451,7 @@ mod test { .unwrap(); let mut context = Context::new(); - context - .stack_mut() - .set_global("a", Value::scalar("else")); + context.stack_mut().set_global("a", Value::scalar("else")); let output = template.render(&mut context).unwrap(); assert_eq!(output, "fourth"); } @@ -521,9 +519,7 @@ mod test { let mut context = Context::new(); let mut obj = Object::new(); obj.insert("Star Wars".into(), Value::scalar("1977")); - context - .stack_mut() - .set_global("movies", Value::Object(obj)); + context.stack_mut().set_global("movies", Value::Object(obj)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "if true"); } @@ -538,9 +534,7 @@ mod test { let mut context = Context::new(); let obj = Object::new(); - context - .stack_mut() - .set_global("movies", Value::Object(obj)); + context.stack_mut().set_global("movies", Value::Object(obj)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "if false"); } @@ -559,9 +553,7 @@ mod test { Value::scalar("Star Trek"), Value::scalar("Alien"), ]; - context - .stack_mut() - .set_global("movies", Value::Array(arr)); + context.stack_mut().set_global("movies", Value::Array(arr)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "if true"); } @@ -576,9 +568,7 @@ mod test { let mut context = Context::new(); let arr = vec![Value::scalar("Alien")]; - context - .stack_mut() - .set_global("movies", Value::Array(arr)); + context.stack_mut().set_global("movies", Value::Array(arr)); let output = template.render(&mut context).unwrap(); assert_eq!(output, "if false"); } diff --git a/src/tags/include_tag.rs b/src/tags/include_tag.rs index 819a305b1..072d6bcc4 100644 --- a/src/tags/include_tag.rs +++ b/src/tags/include_tag.rs @@ -46,7 +46,8 @@ pub fn include_tag( arg => return Err(unexpected_token_error("string", arg)), }; - let partial = parse_partial(name, options).trace_with(|| format!("{{% include {} %}}", name))?; + let partial = + parse_partial(name, options).trace_with(|| format!("{{% include {} %}}", name))?; Ok(Box::new(Include { name: name.to_owned(), diff --git a/tests/multithreading.rs b/tests/multithreading.rs index 13c6240f6..843191f87 100644 --- a/tests/multithreading.rs +++ b/tests/multithreading.rs @@ -39,8 +39,7 @@ numTwo: {} .expect(&format!( "Expected output file does not exist: {}", output_file - )) - .read_to_string(&mut comp) + )).read_to_string(&mut comp) .expect(&format!("Failed to read file: {}", output_file)); assert_diff!(&comp, &output, " ", 0); From a7fee2de0903d6f18db8eb9d1948a248ec059b5a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 3 Oct 2018 18:16:12 -0600 Subject: [PATCH 7/9] chore: Fix warning --- liquid-interpreter/src/variable.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/liquid-interpreter/src/variable.rs b/liquid-interpreter/src/variable.rs index 1c0bed083..787f7f6c6 100644 --- a/liquid-interpreter/src/variable.rs +++ b/liquid-interpreter/src/variable.rs @@ -21,6 +21,7 @@ impl Variable { Self { path } } + /// The path to the variable in the stack. pub fn path(&self) -> &Path { &self.path } From c27e337b2fd559ae157e0975aff8e1794ab385a3 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 3 Oct 2018 18:25:14 -0600 Subject: [PATCH 8/9] chore: Clippy --- liquid-value/src/ser.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/liquid-value/src/ser.rs b/liquid-value/src/ser.rs index e042b8c90..74b293fec 100644 --- a/liquid-value/src/ser.rs +++ b/liquid-value/src/ser.rs @@ -121,7 +121,7 @@ impl serde::Serializer for Serializer { #[inline] fn serialize_f32(self, value: f32) -> Result { - self.serialize_f64(value as f64) + self.serialize_f64(f64::from(value)) } #[inline] @@ -142,7 +142,7 @@ impl serde::Serializer for Serializer { } fn serialize_bytes(self, value: &[u8]) -> Result { - let vec = value.iter().map(|&b| Value::scalar(b as i32)).collect(); + let vec = value.iter().map(|&b| Value::scalar(i32::from(b))).collect(); Ok(Value::Array(vec)) } From 992ef4bb09dc091d92e01953d6be8d5c7dcaf984 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 3 Oct 2018 19:30:03 -0600 Subject: [PATCH 9/9] chore: Upgrade rustfmt --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2d21eceda..f0b8f34e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,13 +8,13 @@ rust: matrix: include: - env: RUSTFMT - rust: 1.28.0 # `stable`: Locking down for consistent behavior + rust: 1.29.1 # `stable`: Locking down for consistent behavior install: - rustup component add rustfmt-preview script: - cargo fmt -- --check - env: RUSTFLAGS="-D warnings" - rust: 1.28.0 # `stable`: Locking down for consistent behavior + rust: 1.29.1 # `stable`: Locking down for consistent behavior install: script: - cargo check --tests