From 7ff0fcd04d2570aea5338e03128de62e494bee62 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 15 Jan 2018 07:33:38 -0700 Subject: [PATCH] feat(Value): Control key order Fixes #159 --- Cargo.toml | 8 ++++++++ src/filters/mod.rs | 4 ++-- src/lib.rs | 2 ++ src/tags/for_block.rs | 5 ++--- src/tags/if_block.rs | 6 +++--- src/value/values.rs | 21 +++++++++++++++++++-- 6 files changed, 36 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ec59ef6f..a3e547b90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,12 @@ default = ["extra-filters", "serde"] cli = ["clap", "error-chain", "serde_yaml"] extra-filters = [] dev = [] +# Ensure keys in `Value`s `Object`s to be sorted. +# Mutually exclusive with `object_order_preserved` +object_sorted = [] +# Ensure the order that keys in `Value`s `Object`s are inserted is preserved +# Mutually exclusive with `object_sorted` +object_order_preserved = ["linked-hash-map"] [dependencies] regex = "0.2" @@ -44,6 +50,8 @@ error-chain = { version = "0.11.0", optional = true } serde_yaml = { version = "0.7", optional = true } serde_json = { version = "1.0", optional = true } +linked-hash-map = { version = "0.5", optional = true } + [build-dependencies] skeptic = "0.13.2" serde = { version = "1.0", features = ["derive"] } diff --git a/src/filters/mod.rs b/src/filters/mod.rs index 6be1188de..4fda1bd4c 100644 --- a/src/filters/mod.rs +++ b/src/filters/mod.rs @@ -598,7 +598,7 @@ pub fn pluralize(input: &Value, args: &[Value]) -> FilterResult { #[cfg(test)] mod tests { - use std::collections::HashMap; + use value::Object; use super::*; macro_rules! unit { @@ -1371,7 +1371,7 @@ mod tests { &[Value::scalar(1_f32)]), Value::Array(vec![tos!("")])); assert_eq!(unit!(default, - Value::Object(HashMap::new()), + Value::Object(Object::new()), &[Value::scalar(1_f32)]), Value::scalar(1_f32)); assert_eq!(unit!(default, Value::scalar(false), &[Value::scalar(1_f32)]), diff --git a/src/lib.rs b/src/lib.rs index 07cbdc2e3..5b27f8d00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,8 @@ extern crate lazy_static; extern crate serde; #[cfg(test)] extern crate serde_yaml; +#[cfg(feature = "object_order_preserved")] +extern crate linked_hash_map; mod error; mod filters; diff --git a/src/tags/for_block.rs b/src/tags/for_block.rs index 5141e7610..f959122d5 100644 --- a/src/tags/for_block.rs +++ b/src/tags/for_block.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::slice::Iter; use error::{Error, Result}; @@ -11,7 +10,7 @@ use compiler::Element; use compiler::LiquidOptions; use compiler::Token; use compiler::{parse, expect, split_block}; -use value::Value; +use value::{Value, Object}; #[derive(Clone, Debug)] enum Range { @@ -95,7 +94,7 @@ impl Renderable for For { range_len => { let mut ret = String::default(); context.run_in_scope(|mut scope| { - let mut helper_vars: HashMap = HashMap::new(); + let mut helper_vars = Object::new(); helper_vars.insert("length".to_owned(), Value::scalar(range_len as i32)); for (i, v) in slice.iter().enumerate() { diff --git a/src/tags/if_block.rs b/src/tags/if_block.rs index d7fd7c19d..6c248ce2d 100644 --- a/src/tags/if_block.rs +++ b/src/tags/if_block.rs @@ -155,9 +155,9 @@ pub fn if_block(_tag_name: &str, #[cfg(test)] mod test { - use std::collections::HashMap; use super::*; use value::Value; + use value::Object; use compiler; use interpreter; @@ -418,7 +418,7 @@ mod test { .unwrap(); let mut context = Context::new(); - let mut obj = HashMap::new(); + let mut obj = Object::new(); obj.insert("Star Wars".to_owned(), Value::scalar("1977")); context.set_global_val("movies", Value::Object(obj)); let output = template.render(&mut context).unwrap(); @@ -434,7 +434,7 @@ mod test { .unwrap(); let mut context = Context::new(); - let obj = HashMap::new(); + let obj = Object::new(); context.set_global_val("movies", Value::Object(obj)); let output = template.render(&mut context).unwrap(); assert_eq!(output, Some("if false".to_owned())); diff --git a/src/value/values.rs b/src/value/values.rs index 9b4e8edf7..d54f356cc 100644 --- a/src/value/values.rs +++ b/src/value/values.rs @@ -1,11 +1,28 @@ -use std::collections::HashMap; use std::cmp::Ordering; use std::fmt; use std::borrow; +#[cfg(feature = "object_sorted")] +use std::collections::BTreeMap; + +#[cfg(feature = "object_order_preserved")] +use linked_hash_map::LinkedHashMap; + +#[cfg(not(any(feature = "object_order_preserved", feature = "object_sorted")))] +use std::collections::HashMap; + use super::Index; use super::Scalar; +#[cfg(feature = "object_sorted")] +type MapImpl = BTreeMap; + +#[cfg(feature = "object_order_preserved")] +type MapImpl = LinkedHashMap; + +#[cfg(not(any(feature = "object_order_preserved", feature = "object_sorted")))] +type MapImpl = HashMap; + /// An enum to represent different value types #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -21,7 +38,7 @@ pub enum Value { pub type Array = Vec; /// Type representing a Liquid object, payload of the `Value::Object` variant -pub type Object = HashMap; +pub type Object = MapImpl; impl Value { pub fn scalar>(value: T) -> Self {