Skip to content

Commit

Permalink
fix(error): look of values/variables
Browse files Browse the repository at this point in the history
Fixes #258
  • Loading branch information
epage committed Dec 16, 2018
1 parent 6c0f518 commit 111160c
Show file tree
Hide file tree
Showing 16 changed files with 168 additions and 58 deletions.
6 changes: 3 additions & 3 deletions liquid-compiler/src/filter_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ impl FilterCall {
.context_key("filter")
.value_with(|| format!("{}", self).into())
.context_key("input")
.value_with(|| format!("{}", &entry).into())
.value_with(|| format!("{}", entry.source()).into())
.context_key("args")
.value_with(|| itertools::join(&arguments, ", ").into())
.value_with(|| itertools::join(arguments.iter().map(Value::source), ", ").into())
}
}

Expand Down Expand Up @@ -101,7 +101,7 @@ impl fmt::Display for FilterChain {
impl Renderable for FilterChain {
fn render_to(&self, writer: &mut Write, context: &mut Context) -> Result<()> {
let entry = self.evaluate(context)?;
write!(writer, "{}", entry).chain("Failed to render")?;
write!(writer, "{}", entry.to_str()).chain("Failed to render")?;
Ok(())
}
}
2 changes: 1 addition & 1 deletion liquid-interpreter/src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl Expression {
impl fmt::Display for Expression {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Expression::Literal(ref x) => write!(f, "{}", x),
Expression::Literal(ref x) => write!(f, "{}", x.source()),
Expression::Variable(ref x) => write!(f, "{}", x),
}
}
Expand Down
8 changes: 5 additions & 3 deletions liquid-interpreter/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use itertools;
use liquid_error::{Error, Result};
use liquid_value::Object;
use liquid_value::PathRef;
use liquid_value::ScalarCow;
use liquid_value::Value;

/// Immutable view into a template's global variables.
Expand Down Expand Up @@ -61,12 +62,13 @@ impl ValueStore for Object {
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 subpath = itertools::join(subpath.iter().map(ScalarCow::render), ".");
let requested = &path[subpath_end];
let available = itertools::join(parent.keys(), ", ");
let available: Vec<_> = parent.keys().collect();
let available = itertools::join(available.iter().map(ScalarCow::render), ", ");
return Err(Error::with_msg("Unknown index")
.context("variable", subpath)
.context("requested index", format!("{}", requested))
.context("requested index", format!("{}", requested.render()))
.context("available indexes", available));
}
}
Expand Down
10 changes: 5 additions & 5 deletions liquid-interpreter/src/variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl Variable {
let v = expr.evaluate(context)?;
let s = v
.as_scalar()
.ok_or_else(|| Error::with_msg(format!("Expected scalar, found `{}`", v)))?
.ok_or_else(|| Error::with_msg(format!("Expected scalar, found `{}`", v.source())))?
.as_ref();
path.push(s);
}
Expand All @@ -73,7 +73,7 @@ impl Extend<Expression> for Variable {

impl fmt::Display for Variable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.variable)?;
write!(f, "{}", self.variable.render())?;
for index in self.indexes.iter() {
write!(f, "[{}]", index)?;
}
Expand Down Expand Up @@ -105,7 +105,7 @@ test_a: ["test"]
let context = ContextBuilder::new().set_globals(&globals).build();
let actual = var.evaluate(&context).unwrap();
let actual = context.stack().get(&actual).unwrap();
assert_eq!(actual.to_string(), "test");
assert_eq!(actual.to_str(), "test");
}

#[test]
Expand All @@ -123,7 +123,7 @@ test_a: ["test1", "test2"]
let context = ContextBuilder::new().set_globals(&globals).build();
let actual = var.evaluate(&context).unwrap();
let actual = context.stack().get(&actual).unwrap();
assert_eq!(actual.to_string(), "test2");
assert_eq!(actual.to_str(), "test2");
}

#[test]
Expand All @@ -142,6 +142,6 @@ test_a:
let context = ContextBuilder::new().set_globals(&globals).build();
let actual = var.evaluate(&context).unwrap();
let actual = context.stack().get(&actual).unwrap();
assert_eq!(actual.to_string(), "5");
assert_eq!(actual.to_str(), "5");
}
}
2 changes: 1 addition & 1 deletion liquid-value/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl<'s> AsRef<[ScalarCow<'s>]> for Path<'s> {

impl<'s> fmt::Display for Path<'s> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let data = itertools::join(self.iter(), ".");
let data = itertools::join(self.iter().map(ScalarCow::render), ".");
write!(f, "{}", data)
}
}
Expand Down
50 changes: 43 additions & 7 deletions liquid-value/src/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ impl<'s> ScalarCow<'s> {
value.into()
}

/// A `Display` for a `Scalar` as source code.
pub fn source(&self) -> ScalarSource {
ScalarSource(&self.0)
}

/// A `Display` for a `Scalar` rendered for the user.
pub fn render(&self) -> ScalarRendered {
ScalarRendered(&self.0)
}

/// Create an owned version of the value.
pub fn into_owned(self) -> Self {
match self.0 {
Expand Down Expand Up @@ -140,13 +150,6 @@ impl<'s> ScalarCow<'s> {
}
}

impl<'s> fmt::Display for ScalarCow<'s> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let data = self.to_str();
write!(f, "{}", data)
}
}

impl<'s> From<i32> for ScalarCow<'s> {
fn from(s: i32) -> Self {
ScalarCow {
Expand Down Expand Up @@ -295,6 +298,39 @@ impl<'s> PartialOrd<str> for ScalarCow<'s> {
}
}

/// A `Display` for a `Scalar` as source code.
#[derive(Debug)]
pub struct ScalarSource<'s>(&'s ScalarCowEnum<'s>);

impl<'s> fmt::Display for ScalarSource<'s> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
ScalarCowEnum::Integer(ref x) => write!(f, "{}", x),
ScalarCowEnum::Float(ref x) => write!(f, "{}", x),
ScalarCowEnum::Bool(ref x) => write!(f, "{}", x),
ScalarCowEnum::Date(ref x) => write!(f, "{}", x.format(DATE_FORMAT)),
ScalarCowEnum::Str(ref x) => write!(f, r#""{}""#, x),
}
}
}

/// A `Display` for a `Scalar` rendered for the user.
#[derive(Debug)]
pub struct ScalarRendered<'s>(&'s ScalarCowEnum<'s>);

impl<'s> fmt::Display for ScalarRendered<'s> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Must match `ScalarCow::to_str`
match self.0 {
ScalarCowEnum::Integer(ref x) => write!(f, "{}", x),
ScalarCowEnum::Float(ref x) => write!(f, "{}", x),
ScalarCowEnum::Bool(ref x) => write!(f, "{}", x),
ScalarCowEnum::Date(ref x) => write!(f, "{}", x.format(DATE_FORMAT)),
ScalarCowEnum::Str(ref x) => write!(f, "{}", x),
}
}
}

fn scalar_eq<'s>(lhs: &ScalarCow<'s>, rhs: &ScalarCow<'s>) -> bool {
match (&lhs.0, &rhs.0) {
(&ScalarCowEnum::Integer(x), &ScalarCowEnum::Integer(y)) => x == y,
Expand Down
94 changes: 80 additions & 14 deletions liquid-value/src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::borrow;
use std::cmp::Ordering;
use std::fmt;

use itertools;

use super::map;
use super::Scalar;
use super::ScalarCow;
Expand Down Expand Up @@ -47,17 +49,30 @@ impl Value {
Value::Nil
}

/// A `Display` for a `Scalar` as source code.
pub fn source(&self) -> ValueSource {
ValueSource(&self)
}

/// A `Display` for a `Value` rendered for the user.
pub fn render(&self) -> ValueRendered {
ValueRendered(&self)
}

/// Interpret as a string.
pub fn to_str(&self) -> borrow::Cow<str> {
match *self {
Value::Scalar(ref x) => x.to_str(),
Value::Array(ref x) => {
let arr: Vec<String> = x.iter().map(|v| v.to_string()).collect();
borrow::Cow::Owned(arr.join(", "))
let arr: Vec<_> = x.iter().map(|v| v.render()).collect();
borrow::Cow::Owned(itertools::join(arr, ""))
}
Value::Object(ref x) => {
let arr: Vec<String> = x.iter().map(|(k, v)| format!("{}: {}", k, v)).collect();
borrow::Cow::Owned(arr.join(", "))
let arr: Vec<_> = x
.iter()
.map(|(k, v)| format!("{}{}", k, v.render()))
.collect();
borrow::Cow::Owned(itertools::join(arr, ""))
}
Value::Nil | Value::Empty | Value::Blank => borrow::Cow::Borrowed(""),
}
Expand Down Expand Up @@ -330,13 +345,6 @@ impl Default for Value {
}
}

impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let data = self.to_str();
write!(f, "{}", data)
}
}

impl PartialEq<Value> for Value {
fn eq(&self, other: &Self) -> bool {
value_eq(self, other)
Expand All @@ -351,6 +359,61 @@ impl PartialOrd<Value> for Value {
}
}

/// A `Display` for a `Scalar` as source code.
#[derive(Debug)]
pub struct ValueSource<'s>(&'s Value);

impl<'s> fmt::Display for ValueSource<'s> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
Value::Scalar(ref x) => write!(f, "{}", x.render())?,
Value::Array(ref x) => {
write!(f, "[")?;
for item in x {
write!(f, "{}, ", item.render())?;
}
write!(f, "]")?;
}
Value::Object(ref x) => {
write!(f, "{{")?;
for (k, v) in x {
write!(f, r#""{}": {}, "#, k, v.render())?;
}
write!(f, "}}")?;
}
Value::Nil => write!(f, "nil")?,
Value::Empty => write!(f, "empty")?,
Value::Blank => write!(f, "blank")?,
}
Ok(())
}
}

/// A `Display` for a `Value` rendered for the user.
#[derive(Debug)]
pub struct ValueRendered<'s>(&'s Value);

impl<'s> fmt::Display for ValueRendered<'s> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Must match `Value::to_str`
match self.0 {
Value::Scalar(ref x) => write!(f, "{}", x.render())?,
Value::Array(ref x) => {
for item in x {
write!(f, "{}", item.render())?;
}
}
Value::Object(ref x) => {
for (k, v) in x {
write!(f, "{}{}", k, v.render())?;
}
}
Value::Nil | Value::Empty | Value::Blank => (),
}
Ok(())
}
}

fn value_eq(lhs: &Value, rhs: &Value) -> bool {
match (lhs, rhs) {
(&Value::Scalar(ref x), &Value::Scalar(ref y)) => x == y,
Expand Down Expand Up @@ -413,7 +476,8 @@ mod test {
#[test]
fn test_to_string_scalar() {
let val = Value::scalar(42f64);
assert_eq!(&val.to_string(), "42");
assert_eq!(&val.render().to_string(), "42");
assert_eq!(&val.to_str(), "42");
}

#[test]
Expand All @@ -423,14 +487,16 @@ mod test {
Value::scalar("test"),
Value::scalar(5.3),
]);
assert_eq!(&val.to_string(), "3, test, 5.3");
assert_eq!(&val.render().to_string(), "3test5.3");
assert_eq!(&val.to_str(), "3test5.3");
}

// TODO make a test for object, remember values are in arbitrary orders in HashMaps

#[test]
fn test_to_string_nil() {
assert_eq!(&Value::nil().to_string(), "");
assert_eq!(&Value::nil().render().to_string(), "");
assert_eq!(&Value::nil().to_str(), "");
}

#[test]
Expand Down
6 changes: 3 additions & 3 deletions src/filters/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,17 @@ pub fn array_to_sentence_string(input: &Value, args: &[Value]) -> FilterResult {

let mut sentence = array
.next()
.map(|v| v.to_string())
.map(|v| v.to_str().into_owned())
.unwrap_or_else(|| "".to_string());

let last = array.next_back();

for value in array {
write!(sentence, ", {}", value).expect("It should be safe to write to a string.");
write!(sentence, ", {}", value.render()).expect("It should be safe to write to a string.");
}

if let Some(last) = last {
write!(sentence, ", {} {}", connector, last)
write!(sentence, ", {} {}", connector, last.render())
.expect("It should be safe to write to a string.");
}

Expand Down
2 changes: 1 addition & 1 deletion src/filters/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub fn strip_html(input: &Value, args: &[Value]) -> FilterResult {
}
check_args_len(args, 0, 0)?;

let input = input.to_string();
let input = input.to_str().into_owned();

let result = MATCHERS.iter().fold(input, |acc, matcher| {
matcher.replace_all(&acc, "").into_owned()
Expand Down
4 changes: 2 additions & 2 deletions src/filters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ pub fn remove_first(input: &Value, args: &[Value]) -> FilterResult {
pub fn append(input: &Value, args: &[Value]) -> FilterResult {
check_args_len(args, 1, 0)?;

let mut input = input.to_string();
let mut input = input.to_str().into_owned();

let string = args[0].to_str();

Expand Down Expand Up @@ -529,7 +529,7 @@ pub fn prepend(input: &Value, args: &[Value]) -> FilterResult {

let input = input.to_str();

let mut string = args[0].to_string();
let mut string = args[0].to_str().into_owned();

string.push_str(input.as_ref());

Expand Down
4 changes: 2 additions & 2 deletions src/tags/case_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl Renderable for Case {
.trace_with(|| case.trace().into())
.trace_with(|| self.trace().into())
.context_key_with(|| self.target.to_string().into())
.value_with(|| value.to_string().into());
.value_with(|| value.to_str().into_owned().into());
}
}

Expand All @@ -74,7 +74,7 @@ impl Renderable for Case {
.trace("{{% else %}}")
.trace_with(|| self.trace().into())
.context_key_with(|| self.target.to_string().into())
.value_with(|| value.to_string().into());
.value_with(|| value.to_str().into_owned().into());
}

Ok(())
Expand Down

0 comments on commit 111160c

Please sign in to comment.