liquid_interpreter/
store.rs1use std::fmt;
2
3use itertools;
4use liquid_error::{Error, Result};
5use liquid_value::Object;
6use liquid_value::PathRef;
7use liquid_value::ScalarCow;
8use liquid_value::Value;
9
10pub trait ValueStore: fmt::Debug {
12 fn contains_root(&self, name: &str) -> bool;
14
15 fn roots(&self) -> Vec<&str>;
17
18 fn contains_variable(&self, path: PathRef) -> bool;
24
25 fn try_get_variable<'a>(&'a self, path: PathRef) -> Option<&'a Value>;
31
32 fn get_variable<'a>(&'a self, path: PathRef) -> Result<&'a Value>;
38}
39
40impl ValueStore for Object {
41 fn contains_root(&self, name: &str) -> bool {
42 self.contains_key(name)
43 }
44
45 fn roots(&self) -> Vec<&str> {
46 self.keys().map(|s| s.as_ref()).collect()
47 }
48
49 fn contains_variable(&self, path: PathRef) -> bool {
50 get_variable_option(self, path).is_some()
51 }
52
53 fn try_get_variable<'a>(&'a self, path: PathRef) -> Option<&'a Value> {
54 get_variable_option(self, path)
55 }
56
57 fn get_variable<'a>(&'a self, path: PathRef) -> Result<&'a Value> {
58 if let Some(res) = self.try_get_variable(path) {
59 return Ok(res);
60 } else {
61 for cur_idx in 1..path.len() {
62 let subpath_end = path.len() - cur_idx;
63 let subpath = &path[0..subpath_end];
64 if let Some(parent) = self.try_get_variable(subpath) {
65 let subpath = itertools::join(subpath.iter().map(ScalarCow::render), ".");
66 let requested = &path[subpath_end];
67 let available: Vec<_> = parent.keys().collect();
68 let available = itertools::join(available.iter().map(ScalarCow::render), ", ");
69 return Error::with_msg("Unknown index")
70 .context("variable", subpath)
71 .context("requested index", format!("{}", requested.render()))
72 .context("available indexes", available)
73 .into_err();
74 }
75 }
76
77 let requested = path
78 .get(0)
79 .expect("`Path` guarantees at least one element")
80 .to_str()
81 .into_owned();
82 let available = itertools::join(self.keys(), ", ");
83 return Error::with_msg("Unknown variable")
84 .context("requested variable", requested)
85 .context("available variables", available)
86 .into_err();
87 }
88 }
89}
90
91fn get_variable_option<'o>(obj: &'o Object, path: PathRef) -> Option<&'o Value> {
92 let mut indexes = path.iter();
93 let key = indexes.next()?;
94 let key = key.to_str();
95 let value = obj.get(key.as_ref())?;
96
97 indexes.fold(Some(value), |value, index| {
98 let value = value?;
99 value.get(index)
100 })
101}