2017-07-04 17:12:40 +02:00
|
|
|
// Package render parses and evaluates template strings.
|
2017-07-04 17:03:18 +02:00
|
|
|
package render
|
2017-06-25 17:23:20 +02:00
|
|
|
|
2017-06-25 23:00:00 +02:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2017-06-29 19:41:26 +02:00
|
|
|
"reflect"
|
2017-07-03 18:00:43 +02:00
|
|
|
|
2017-07-05 17:35:07 +02:00
|
|
|
"github.com/osteele/liquid/evaluator"
|
2017-06-25 23:00:00 +02:00
|
|
|
)
|
2017-06-25 17:23:20 +02:00
|
|
|
|
2017-07-06 01:33:24 +02:00
|
|
|
// An Error is an evaluation error during template rendering.
|
|
|
|
type Error string
|
2017-07-05 16:37:46 +02:00
|
|
|
|
2017-07-06 01:33:24 +02:00
|
|
|
func (e Error) Error() string { return string(e) }
|
2017-07-05 16:37:46 +02:00
|
|
|
|
2017-07-06 01:09:59 +02:00
|
|
|
// Errorf creates a render error.
|
2017-07-06 01:33:24 +02:00
|
|
|
func Errorf(format string, a ...interface{}) Error {
|
|
|
|
return Error(fmt.Sprintf(format, a...))
|
2017-07-05 20:24:15 +02:00
|
|
|
}
|
|
|
|
|
2017-07-06 15:21:26 +02:00
|
|
|
// Render renders the render tree.
|
2017-07-07 11:41:37 +02:00
|
|
|
func Render(node Node, w io.Writer, vars map[string]interface{}, c Config) error {
|
|
|
|
// fmt.Println("render", c)
|
|
|
|
return renderNode(node, w, newNodeContext(vars, c))
|
2017-06-25 17:23:20 +02:00
|
|
|
}
|
|
|
|
|
2017-07-06 15:21:26 +02:00
|
|
|
func renderNode(node Node, w io.Writer, ctx nodeContext) error { // nolint: gocyclo
|
2017-07-04 16:25:29 +02:00
|
|
|
switch n := node.(type) {
|
2017-07-06 15:21:26 +02:00
|
|
|
case *BlockNode:
|
2017-07-04 22:48:38 +02:00
|
|
|
cd, ok := ctx.config.findBlockDef(n.Name)
|
2017-07-04 16:25:29 +02:00
|
|
|
if !ok || cd.parser == nil {
|
2017-07-07 11:41:37 +02:00
|
|
|
return Errorf("unknown tag: %s", n.Name)
|
2017-07-04 16:25:29 +02:00
|
|
|
}
|
|
|
|
renderer := n.renderer
|
|
|
|
if renderer == nil {
|
2017-07-07 11:41:37 +02:00
|
|
|
panic(Errorf("unset renderer for %v", n))
|
2017-07-04 16:25:29 +02:00
|
|
|
}
|
2017-07-07 13:30:32 +02:00
|
|
|
return renderer(w, rendererContext{ctx, nil, n})
|
2017-07-06 15:21:26 +02:00
|
|
|
case *RawNode:
|
|
|
|
for _, s := range n.slices {
|
|
|
|
_, err := w.Write([]byte(s))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case *ObjectNode:
|
2017-07-04 16:25:29 +02:00
|
|
|
value, err := ctx.Evaluate(n.expr)
|
2017-06-27 23:40:15 +02:00
|
|
|
if err != nil {
|
2017-07-07 11:41:37 +02:00
|
|
|
return Errorf("%s in %s", err, n.Source)
|
2017-06-27 23:40:15 +02:00
|
|
|
}
|
2017-07-04 16:25:29 +02:00
|
|
|
return writeObject(value, w)
|
2017-07-06 15:21:26 +02:00
|
|
|
case *SeqNode:
|
|
|
|
for _, c := range n.Children {
|
|
|
|
if err := renderNode(c, w, ctx); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2017-07-07 15:32:24 +02:00
|
|
|
case *TagNode:
|
|
|
|
return n.renderer(w, rendererContext{ctx, n, nil})
|
2017-07-06 15:21:26 +02:00
|
|
|
case *TextNode:
|
|
|
|
_, err := w.Write([]byte(n.Source))
|
|
|
|
return err
|
2017-07-04 16:25:29 +02:00
|
|
|
default:
|
2017-07-07 11:41:37 +02:00
|
|
|
panic(Errorf("unknown node type %T", node))
|
2017-06-27 23:40:15 +02:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-06-30 20:51:21 +02:00
|
|
|
// writeObject writes a value used in an object node
|
|
|
|
func writeObject(value interface{}, w io.Writer) error {
|
2017-07-05 17:35:07 +02:00
|
|
|
value = evaluator.ToLiquid(value)
|
2017-06-29 19:41:26 +02:00
|
|
|
if value == nil {
|
2017-06-27 23:29:50 +02:00
|
|
|
return nil
|
|
|
|
}
|
2017-06-29 19:41:26 +02:00
|
|
|
rt := reflect.ValueOf(value)
|
|
|
|
switch rt.Kind() {
|
|
|
|
case reflect.Array, reflect.Slice:
|
|
|
|
for i := 0; i < rt.Len(); i++ {
|
|
|
|
item := rt.Index(i)
|
|
|
|
if item.IsValid() {
|
2017-06-30 20:51:21 +02:00
|
|
|
if err := writeObject(item.Interface(), w); err != nil {
|
2017-06-29 19:41:26 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
_, err := w.Write([]byte(fmt.Sprint(value)))
|
|
|
|
return err
|
|
|
|
}
|
2017-06-25 17:23:20 +02:00
|
|
|
}
|
2017-06-28 02:23:09 +02:00
|
|
|
|
|
|
|
// RenderASTSequence renders a sequence of nodes.
|
2017-07-06 15:21:26 +02:00
|
|
|
func (c nodeContext) RenderSequence(w io.Writer, seq []Node) error {
|
2017-06-28 02:23:09 +02:00
|
|
|
for _, n := range seq {
|
2017-07-04 16:25:29 +02:00
|
|
|
if err := renderNode(n, w, c); err != nil {
|
2017-06-28 02:23:09 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|