Skip to content

Commit

Permalink
feat: Rich Gobals API
Browse files Browse the repository at this point in the history
This is an experiment in what we might need from the `Globals` API once
we support the lookups returning `Cow`s.

BREAKING CHANGE: liquid-interpreter's `Globals` changed APIs.
  • Loading branch information
epage committed Nov 17, 2018
1 parent f202590 commit 385a62f
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 22 deletions.
42 changes: 24 additions & 18 deletions liquid-interpreter/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,30 +171,36 @@ impl<'g> Stack<'g> {
.next()
.ok_or_else(|| Error::with_msg("No variable provided"))?;
let key = key.to_str();
let value = self.get_root(key.as_ref())?;

indexes.fold(Ok(value), |value, index| {
let value = value?;
let child = value.get(index);
let child = child.ok_or_else(|| {
Error::with_msg("Unknown index")
.context("variable", format!("{}", path))
.context("index", format!("{}", index))
})?;
Ok(child)
let frame = self.find_frame(key.as_ref()).ok_or_else(|| {
let key = key.into_owned();
Error::with_msg("Unknown variable").context("variable", key)
})?;

frame.get_variable(path).ok_or_else(|| {
Error::with_msg("Unknown index").context("variable", format!("{}", path))
})
}

fn get_root<'a>(&'a self, name: &str) -> Result<&'a Value> {
fn find_frame<'a>(&'a self, name: &str) -> Option<&'a Globals> {
for frame in self.stack.iter().rev() {
if let Some(rval) = frame.get(name) {
return Ok(rval);
if frame.contains_global(name) {
return Some(frame);
}
}
self.globals
.ok_or_else(|| Error::with_msg("Unknown variable").context("variable", name.to_owned()))
.and_then(|g| g.get(name))
.or_else(|err| self.get_index(name).ok_or_else(|| err))

if self
.globals
.map(|g| g.contains_global(name))
.unwrap_or(false)
{
return self.globals;
}

if self.indexes.contains_global(name) {
return Some(&self.indexes);
}

return None;
}

/// Used by increment and decrement tags
Expand Down
48 changes: 44 additions & 4 deletions liquid-interpreter/src/globals.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,58 @@
use std::fmt;

use error::{Error, Result};
use value::Object;
use value::Path;
use value::Value;

/// Immutable view into a template's global variables.
pub trait Globals: fmt::Debug {
/// Check if global variable exists.
fn contains_global(&self, name: &str) -> bool;

/// Access a global variable.
fn get<'a>(&'a self, name: &str) -> Result<&'a Value>;
fn get_global<'a>(&'a self, name: &str) -> Option<&'a Value>;

/// Check if variable exists.
///
/// Notes to implementers:
/// - Don't forget to reverse-index on negative array indexes
/// - Don't forget about arr.first, arr.last.
fn contains_variable(&self, path: &Path) -> bool;

/// Access a variable.
///
/// Notes to implementers:
/// - Don't forget to reverse-index on negative array indexes
/// - Don't forget about arr.first, arr.last.
fn get_variable<'a>(&'a self, path: &Path) -> Option<&'a Value>;
}

impl Globals for Object {
fn get<'a>(&'a self, name: &str) -> Result<&'a Value> {
fn contains_global(&self, name: &str) -> bool {
self.contains_key(name)
}

fn get_global<'a>(&'a self, name: &str) -> Option<&'a Value> {
self.get(name)
.ok_or_else(|| Error::with_msg("Unknown variable").context("variable", name.to_owned()))
}

fn contains_variable(&self, path: &Path) -> bool {
get_variable_option(self, path).is_some()
}

fn get_variable<'a>(&'a self, path: &Path) -> Option<&'a Value> {
get_variable_option(self, path)
}
}

fn get_variable_option<'o>(obj: &'o Object, path: &Path) -> Option<&'o Value> {
let mut indexes = path.iter();
let key = indexes.next()?;
let key = key.to_str();
let value = obj.get(key.as_ref())?;

indexes.fold(Some(value), |value, index| {
let value = value?;
value.get(index)
})
}

0 comments on commit 385a62f

Please sign in to comment.