1
0
mirror of https://github.com/danog/liquid.git synced 2024-11-30 06:08:57 +01:00
liquid/render/context.go

182 lines
5.6 KiB
Go
Raw Permalink Normal View History

2017-07-04 17:03:18 +02:00
package render
2017-06-26 21:36:05 +02:00
import (
"bytes"
"io"
"io/ioutil"
2020-11-03 01:36:02 +01:00
"os"
"strings"
2022-01-06 18:43:17 +01:00
"github.com/danog/liquid/expressions"
2017-06-26 21:36:05 +02:00
)
// Context provides the rendering context for a tag renderer.
type Context interface {
2017-07-14 18:59:30 +02:00
// Get retrieves the value of a variable from the current lexical environment.
Get(name string) interface{}
2017-07-14 18:59:30 +02:00
// Errorf creates a SourceError, that includes the source location.
// Use this to distinguish errors in the template from implementation errors
// in the template engine.
2017-07-10 17:49:14 +02:00
Errorf(format string, a ...interface{}) Error
2017-07-14 18:59:30 +02:00
// Evaluate evaluates a compiled expression within the current lexical context.
Evaluate(expressions.Expression) (interface{}, error)
// EvaluateString compiles and evaluates a string expression such as “x”, “x < 10", or “a.b | split | first | default: 10”, within the current lexical context.
EvaluateString(string) (interface{}, error)
2017-07-10 13:51:51 +02:00
// ExpandTagArg renders the current tag argument string as a Liquid template.
2017-07-14 18:59:30 +02:00
// It enables the implementation of tags such as Jekyll's "{% include {{ page.my_variable }} %}" andjekyll-avatar's "{% avatar {{page.author}} %}".
2017-07-06 05:25:03 +02:00
ExpandTagArg() (string, error)
2017-07-14 18:59:30 +02:00
// InnerString is the rendered content of the current block.
// It's used in the implementation of the Liquid "capture" tag and the Jekyll "highlght" tag.
InnerString() (string, error)
2017-07-14 18:59:30 +02:00
// RenderBlock is used in the implementation of the built-in control flow tags.
// It's not guaranteed stable.
RenderBlock(io.Writer, *BlockNode) error
// RenderChildren is used in the implementation of the built-in control flow tags.
// It's not guaranteed stable.
2017-07-10 17:49:14 +02:00
RenderChildren(io.Writer) Error
2017-07-14 18:59:30 +02:00
// RenderFile parses and renders a template. It's used in the implementation of the {% include %} tag.
// RenderFile does not cache the compiled template.
2017-07-04 22:48:38 +02:00
RenderFile(string, map[string]interface{}) (string, error)
2017-07-14 18:59:30 +02:00
// Set updates the value of a variable in the current lexical environment.
// It's used in the implementation of the {% assign %} and {% capture %} tags.
2017-07-04 22:48:38 +02:00
Set(name string, value interface{})
2017-07-10 13:51:51 +02:00
// SourceFile retrieves the value set by template.SetSourcePath.
2017-07-14 18:59:30 +02:00
// It's used in the implementation of the {% include %} tag.
2017-07-04 22:48:38 +02:00
SourceFile() string
2017-07-10 13:51:51 +02:00
// TagArgs returns the text of the current tag, not including its name.
// For example, the arguments to {% my_tag a b c %} would be “a b c”.
TagArgs() string
2017-07-14 18:59:30 +02:00
// TagName returns the name of the current tag; for example "my_tag" for {% my_tag a b c %}.
TagName() string
2017-07-14 18:59:30 +02:00
// WrapError creates a new error that records the source location from the current context.
2017-07-10 17:49:14 +02:00
WrapError(err error) Error
2017-06-26 21:36:05 +02:00
}
2017-07-07 13:30:32 +02:00
type rendererContext struct {
ctx nodeContext
2017-07-06 15:40:37 +02:00
node *TagNode
cn *BlockNode
2017-06-26 21:36:05 +02:00
}
2017-07-10 17:49:14 +02:00
func (c rendererContext) Errorf(format string, a ...interface{}) Error {
return renderErrorf(c.node, format, a...)
2017-07-10 17:49:14 +02:00
}
func (c rendererContext) WrapError(err error) Error {
return wrapRenderError(err, c.node)
}
2017-07-14 02:18:23 +02:00
func (c rendererContext) Evaluate(expr expressions.Expression) (out interface{}, err error) {
return c.ctx.Evaluate(expr)
}
// EvaluateString evaluates an expression within the template context.
2017-07-07 13:30:32 +02:00
func (c rendererContext) EvaluateString(source string) (out interface{}, err error) {
2017-07-14 02:18:23 +02:00
return expressions.EvaluateString(source, expressions.NewContext(c.ctx.bindings, c.ctx.config.Config.Config))
}
// Get gets a variable value within an evaluation context.
2017-07-07 13:30:32 +02:00
func (c rendererContext) Get(name string) interface{} {
return c.ctx.bindings[name]
}
2017-07-07 13:30:32 +02:00
func (c rendererContext) ExpandTagArg() (string, error) {
2017-07-04 22:48:38 +02:00
args := c.TagArgs()
if strings.Contains(args, "{{") {
root, err := c.ctx.config.Compile(args, c.node.SourceLoc)
2017-07-04 22:48:38 +02:00
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
2017-07-16 23:43:04 +02:00
err = Render(root, buf, c.ctx.bindings, c.ctx.config)
2017-07-04 22:48:38 +02:00
if err != nil {
return "", err
}
return buf.String(), nil
}
return args, nil
}
// RenderBlock renders a node.
func (c rendererContext) RenderBlock(w io.Writer, b *BlockNode) error {
return c.ctx.RenderSequence(w, b.Body)
}
// RenderChildren renders the current node's children.
2017-07-10 17:49:14 +02:00
func (c rendererContext) RenderChildren(w io.Writer) Error {
if c.cn == nil {
return nil
}
return c.ctx.RenderSequence(w, c.cn.Body)
}
2017-07-07 13:30:32 +02:00
func (c rendererContext) RenderFile(filename string, b map[string]interface{}) (string, error) {
source, err := ioutil.ReadFile(filename)
2020-11-03 01:36:02 +01:00
if err != nil && os.IsNotExist(err) {
// Is it cached?
if cval, ok := c.ctx.config.Cache[filename]; ok {
source = cval
} else {
return "", err
}
} else if err != nil {
return "", err
}
root, err := c.ctx.config.Compile(string(source), c.node.SourceLoc)
if err != nil {
return "", err
}
2017-07-19 16:03:11 +02:00
bindings := map[string]interface{}{}
for k, v := range c.ctx.bindings {
bindings[k] = v
}
2017-07-04 22:48:38 +02:00
for k, v := range b {
2017-07-19 16:03:11 +02:00
bindings[k] = v
2017-07-04 22:48:38 +02:00
}
buf := new(bytes.Buffer)
2017-07-19 16:03:11 +02:00
if err := Render(root, buf, bindings, c.ctx.config); err != nil {
return "", err
}
return buf.String(), nil
}
// InnerString renders the children to a string.
2017-07-07 13:30:32 +02:00
func (c rendererContext) InnerString() (string, error) {
buf := new(bytes.Buffer)
if err := c.RenderChildren(buf); err != nil {
return "", err
}
return buf.String(), nil
}
2017-07-04 22:48:38 +02:00
// Set sets a variable value from an evaluation context.
2017-07-07 13:30:32 +02:00
func (c rendererContext) Set(name string, value interface{}) {
2017-07-04 22:48:38 +02:00
c.ctx.bindings[name] = value
}
2017-07-07 13:30:32 +02:00
func (c rendererContext) SourceFile() string {
return c.node.SourceLoc.Pathname
}
2017-07-07 13:30:32 +02:00
func (c rendererContext) TagArgs() string {
switch {
case c.node != nil:
2017-07-09 17:18:35 +02:00
return c.node.Token.Args
case c.cn != nil:
2017-07-09 17:18:35 +02:00
return c.cn.Token.Args
default:
return ""
}
}
2017-07-07 13:30:32 +02:00
func (c rendererContext) TagName() string {
switch {
case c.node != nil:
2017-07-09 17:18:35 +02:00
return c.node.Token.Name
case c.cn != nil:
2017-07-09 17:18:35 +02:00
return c.cn.Token.Name
default:
return ""
}
2017-06-27 19:18:01 +02:00
}