2017-06-30 20:51:21 +02:00
|
|
|
package chunks
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2017-07-01 03:05:48 +02:00
|
|
|
"io/ioutil"
|
2017-06-30 23:33:36 +02:00
|
|
|
"strings"
|
2017-06-30 20:51:21 +02:00
|
|
|
|
|
|
|
"github.com/osteele/liquid/expressions"
|
|
|
|
)
|
|
|
|
|
|
|
|
// RenderContext provides the rendering context for a tag renderer.
|
|
|
|
type RenderContext interface {
|
2017-07-01 03:05:48 +02:00
|
|
|
Clone() RenderContext
|
2017-06-30 20:51:21 +02:00
|
|
|
Get(name string) interface{}
|
|
|
|
Set(name string, value interface{})
|
|
|
|
Evaluate(expr expressions.Expression) (interface{}, error)
|
|
|
|
EvaluateString(source string) (interface{}, error)
|
|
|
|
EvaluateStatement(tag, source string) (interface{}, error)
|
2017-06-30 23:33:36 +02:00
|
|
|
InnerString() (string, error)
|
|
|
|
ParseTagArgs() (string, error)
|
2017-06-30 22:13:18 +02:00
|
|
|
RenderChild(io.Writer, *ASTControlTag) error
|
2017-06-30 20:51:21 +02:00
|
|
|
RenderChildren(io.Writer) error
|
2017-07-01 03:05:48 +02:00
|
|
|
RenderFile(w io.Writer, filename string) error
|
2017-07-01 16:36:47 +02:00
|
|
|
TagArgs() string
|
|
|
|
TagName() string
|
2017-07-01 03:05:48 +02:00
|
|
|
UpdateBindings(map[string]interface{})
|
2017-06-30 20:51:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type renderContext struct {
|
|
|
|
ctx Context
|
|
|
|
node *ASTFunctional
|
|
|
|
cn *ASTControlTag
|
|
|
|
}
|
|
|
|
|
2017-07-01 03:05:48 +02:00
|
|
|
func (c renderContext) Clone() RenderContext {
|
|
|
|
return renderContext{c.ctx.Clone(), c.node, c.cn}
|
|
|
|
}
|
|
|
|
|
2017-06-30 20:51:21 +02:00
|
|
|
// Evaluate evaluates an expression within the template context.
|
|
|
|
func (c renderContext) Evaluate(expr expressions.Expression) (out interface{}, err error) {
|
|
|
|
return c.ctx.Evaluate(expr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// EvaluateString evaluates an expression within the template context.
|
|
|
|
func (c renderContext) EvaluateString(source string) (out interface{}, err error) {
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
switch e := r.(type) {
|
|
|
|
case expressions.InterpreterError:
|
|
|
|
err = e
|
|
|
|
default:
|
|
|
|
// fmt.Println(string(debug.Stack()))
|
|
|
|
panic(fmt.Errorf("%s during evaluation of %s", e, source))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2017-07-01 03:05:48 +02:00
|
|
|
return expressions.EvaluateString(source, expressions.NewContext(c.ctx.bindings, c.ctx.settings.ExpressionSettings))
|
2017-06-30 20:51:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c renderContext) EvaluateStatement(tag, source string) (interface{}, error) {
|
|
|
|
return c.EvaluateString(fmt.Sprintf("%%%s %s", tag, source))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get gets a variable value within an evaluation context.
|
|
|
|
func (c renderContext) Get(name string) interface{} {
|
2017-07-01 03:05:48 +02:00
|
|
|
return c.ctx.bindings[name]
|
2017-06-30 20:51:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set sets a variable value from an evaluation context.
|
|
|
|
func (c renderContext) Set(name string, value interface{}) {
|
2017-07-01 03:05:48 +02:00
|
|
|
c.ctx.bindings[name] = value
|
2017-06-30 20:51:21 +02:00
|
|
|
}
|
|
|
|
|
2017-06-30 22:13:18 +02:00
|
|
|
// RenderChild renders a node.
|
|
|
|
func (c renderContext) RenderChild(w io.Writer, b *ASTControlTag) error {
|
|
|
|
return c.ctx.RenderASTSequence(w, b.Body)
|
|
|
|
}
|
|
|
|
|
2017-06-30 20:51:21 +02:00
|
|
|
// RenderChildren renders the current node's children.
|
|
|
|
func (c renderContext) RenderChildren(w io.Writer) error {
|
|
|
|
if c.cn == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return c.ctx.RenderASTSequence(w, c.cn.Body)
|
|
|
|
}
|
|
|
|
|
2017-07-01 03:05:48 +02:00
|
|
|
func (c renderContext) RenderFile(w io.Writer, filename string) error {
|
|
|
|
source, err := ioutil.ReadFile(filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ast, err := c.ctx.settings.Parse(string(source))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return ast.Render(w, c.ctx)
|
|
|
|
}
|
|
|
|
|
2017-06-30 20:51:21 +02:00
|
|
|
// InnerString renders the children to a string.
|
|
|
|
func (c renderContext) InnerString() (string, error) {
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
if err := c.RenderChildren(buf); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return buf.String(), nil
|
|
|
|
}
|
2017-06-30 23:33:36 +02:00
|
|
|
|
|
|
|
func (c renderContext) ParseTagArgs() (string, error) {
|
2017-07-01 16:36:47 +02:00
|
|
|
args := c.TagArgs()
|
2017-06-30 23:33:36 +02:00
|
|
|
if strings.Contains(args, "{{") {
|
|
|
|
p, err := c.ctx.settings.Parse(args)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
err = p.Render(buf, c.ctx)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return buf.String(), nil
|
|
|
|
}
|
|
|
|
return args, nil
|
|
|
|
}
|
2017-07-01 03:05:48 +02:00
|
|
|
|
|
|
|
func (c renderContext) UpdateBindings(bindings map[string]interface{}) {
|
|
|
|
for k, v := range bindings {
|
|
|
|
c.ctx.bindings[k] = v
|
|
|
|
}
|
|
|
|
}
|
2017-07-01 16:36:47 +02:00
|
|
|
|
|
|
|
func (c renderContext) TagArgs() string {
|
|
|
|
switch {
|
|
|
|
case c.node != nil:
|
|
|
|
return c.node.Chunk.Args
|
|
|
|
case c.cn != nil:
|
|
|
|
return c.cn.Chunk.Args
|
|
|
|
default:
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c renderContext) TagName() string {
|
|
|
|
switch {
|
|
|
|
case c.node != nil:
|
|
|
|
return c.node.Chunk.Name
|
|
|
|
case c.cn != nil:
|
|
|
|
return c.cn.Chunk.Name
|
|
|
|
default:
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|