Skip to content

Commit 7ff0fcd

Browse files
committed
feat(Value): Control key order
Fixes #159
1 parent 8c43de8 commit 7ff0fcd

File tree

6 files changed

+36
-10
lines changed

6 files changed

+36
-10
lines changed

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ default = ["extra-filters", "serde"]
2929
cli = ["clap", "error-chain", "serde_yaml"]
3030
extra-filters = []
3131
dev = []
32+
# Ensure keys in `Value`s `Object`s to be sorted.
33+
# Mutually exclusive with `object_order_preserved`
34+
object_sorted = []
35+
# Ensure the order that keys in `Value`s `Object`s are inserted is preserved
36+
# Mutually exclusive with `object_sorted`
37+
object_order_preserved = ["linked-hash-map"]
3238

3339
[dependencies]
3440
regex = "0.2"
@@ -44,6 +50,8 @@ error-chain = { version = "0.11.0", optional = true }
4450
serde_yaml = { version = "0.7", optional = true }
4551
serde_json = { version = "1.0", optional = true }
4652

53+
linked-hash-map = { version = "0.5", optional = true }
54+
4755
[build-dependencies]
4856
skeptic = "0.13.2"
4957
serde = { version = "1.0", features = ["derive"] }

src/filters/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ pub fn pluralize(input: &Value, args: &[Value]) -> FilterResult {
598598
#[cfg(test)]
599599
mod tests {
600600

601-
use std::collections::HashMap;
601+
use value::Object;
602602
use super::*;
603603

604604
macro_rules! unit {
@@ -1371,7 +1371,7 @@ mod tests {
13711371
&[Value::scalar(1_f32)]),
13721372
Value::Array(vec![tos!("")]));
13731373
assert_eq!(unit!(default,
1374-
Value::Object(HashMap::new()),
1374+
Value::Object(Object::new()),
13751375
&[Value::scalar(1_f32)]),
13761376
Value::scalar(1_f32));
13771377
assert_eq!(unit!(default, Value::scalar(false), &[Value::scalar(1_f32)]),

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ extern crate lazy_static;
4444
extern crate serde;
4545
#[cfg(test)]
4646
extern crate serde_yaml;
47+
#[cfg(feature = "object_order_preserved")]
48+
extern crate linked_hash_map;
4749

4850
mod error;
4951
mod filters;

src/tags/for_block.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::collections::HashMap;
21
use std::slice::Iter;
32

43
use error::{Error, Result};
@@ -11,7 +10,7 @@ use compiler::Element;
1110
use compiler::LiquidOptions;
1211
use compiler::Token;
1312
use compiler::{parse, expect, split_block};
14-
use value::Value;
13+
use value::{Value, Object};
1514

1615
#[derive(Clone, Debug)]
1716
enum Range {
@@ -95,7 +94,7 @@ impl Renderable for For {
9594
range_len => {
9695
let mut ret = String::default();
9796
context.run_in_scope(|mut scope| {
98-
let mut helper_vars: HashMap<String, Value> = HashMap::new();
97+
let mut helper_vars = Object::new();
9998
helper_vars.insert("length".to_owned(), Value::scalar(range_len as i32));
10099

101100
for (i, v) in slice.iter().enumerate() {

src/tags/if_block.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ pub fn if_block(_tag_name: &str,
155155

156156
#[cfg(test)]
157157
mod test {
158-
use std::collections::HashMap;
159158
use super::*;
160159
use value::Value;
160+
use value::Object;
161161
use compiler;
162162
use interpreter;
163163

@@ -418,7 +418,7 @@ mod test {
418418
.unwrap();
419419

420420
let mut context = Context::new();
421-
let mut obj = HashMap::new();
421+
let mut obj = Object::new();
422422
obj.insert("Star Wars".to_owned(), Value::scalar("1977"));
423423
context.set_global_val("movies", Value::Object(obj));
424424
let output = template.render(&mut context).unwrap();
@@ -434,7 +434,7 @@ mod test {
434434
.unwrap();
435435

436436
let mut context = Context::new();
437-
let obj = HashMap::new();
437+
let obj = Object::new();
438438
context.set_global_val("movies", Value::Object(obj));
439439
let output = template.render(&mut context).unwrap();
440440
assert_eq!(output, Some("if false".to_owned()));

src/value/values.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
1-
use std::collections::HashMap;
21
use std::cmp::Ordering;
32
use std::fmt;
43
use std::borrow;
54

5+
#[cfg(feature = "object_sorted")]
6+
use std::collections::BTreeMap;
7+
8+
#[cfg(feature = "object_order_preserved")]
9+
use linked_hash_map::LinkedHashMap;
10+
11+
#[cfg(not(any(feature = "object_order_preserved", feature = "object_sorted")))]
12+
use std::collections::HashMap;
13+
614
use super::Index;
715
use super::Scalar;
816

17+
#[cfg(feature = "object_sorted")]
18+
type MapImpl<K, V> = BTreeMap<K, V>;
19+
20+
#[cfg(feature = "object_order_preserved")]
21+
type MapImpl<K, V> = LinkedHashMap<K, V>;
22+
23+
#[cfg(not(any(feature = "object_order_preserved", feature = "object_sorted")))]
24+
type MapImpl<K, V> = HashMap<K, V>;
25+
926
/// An enum to represent different value types
1027
#[derive(Clone, Debug)]
1128
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -21,7 +38,7 @@ pub enum Value {
2138
pub type Array = Vec<Value>;
2239

2340
/// Type representing a Liquid object, payload of the `Value::Object` variant
24-
pub type Object = HashMap<String, Value>;
41+
pub type Object = MapImpl<String, Value>;
2542

2643
impl Value {
2744
pub fn scalar<T: Into<Scalar>>(value: T) -> Self {

0 commit comments

Comments
 (0)