diff --git a/liquid-interpreter/src/globals.rs b/liquid-interpreter/src/globals.rs index a5301f8c7..6a9d1f695 100644 --- a/liquid-interpreter/src/globals.rs +++ b/liquid-interpreter/src/globals.rs @@ -54,10 +54,28 @@ impl Globals for Object { } fn get_variable<'a>(&'a self, path: PathRef) -> Result<&'a Value> { - self.try_get_variable(path).ok_or_else(|| { - let path = itertools::join(path.iter(), "."); - Error::with_msg("Unknown index").context("variable", format!("{}", path)) - }) + if let Some(res) = self.try_get_variable(path) { + return Ok(res); + } else { + for cur_idx in 1..path.len() { + let subpath_end = path.len() - cur_idx; + let subpath = &path[0..subpath_end]; + if let Some(parent) = self.try_get_variable(subpath) { + let subpath = itertools::join(subpath.iter(), "."); + let index = &path[subpath_end]; + let available = itertools::join(parent.keys(), ", "); + return Err(Error::with_msg("Unknown index") + .context("variable", format!("{}", subpath)) + .context("requested index", format!("{}", index)) + .context("available indexes", format!("{}", available))); + } + } + + let available = itertools::join(self.keys(), ", "); + return Err(Error::with_msg("Unknown variable") + .context("requested variable", path[0].to_str().into_owned()) + .context("available variables", available)); + } } } diff --git a/liquid-value/src/scalar.rs b/liquid-value/src/scalar.rs index d31790c64..b77cb92cb 100644 --- a/liquid-value/src/scalar.rs +++ b/liquid-value/src/scalar.rs @@ -174,6 +174,14 @@ impl From<&'static str> for Scalar { } } +impl From> for Scalar { + fn from(s: borrow::Cow<'static, str>) -> Self { + Scalar { + 0: ScalarEnum::Str(s), + } + } +} + impl PartialEq for Scalar { fn eq(&self, other: &Self) -> bool { match (&self.0, &other.0) { diff --git a/liquid-value/src/values.rs b/liquid-value/src/values.rs index ba40c63b3..4d827cff6 100644 --- a/liquid-value/src/values.rs +++ b/liquid-value/src/values.rs @@ -213,6 +213,27 @@ impl Value { } } + /// All keys + pub fn keys(&self) -> Vec { + match *self { + Value::Array(ref x) => { + let start: i32 = 0; + let end = x.len() as i32; + let mut keys: Vec<_> = (start..end).map(|i| Scalar::new(i)).collect(); + keys.push(Scalar::new("first")); + keys.push(Scalar::new("last")); + keys + } + Value::Object(ref x) => x.keys().map(|s| { + match *s { + borrow::Cow::Borrowed(s) => Scalar::new(s), + borrow::Cow::Owned(ref s) => Scalar::new(s.to_owned()), + } + }).collect(), + _ => vec![], + } + } + /// Access a contained `Value`. pub fn get<'s>(&'s self, index: &Scalar) -> Option<&'s Self> { match *self {