liquid icon indicating copy to clipboard operation
liquid copied to clipboard

Add FRender to allow rendering into a custom io.Writer

Open jamslinger opened this issue 10 months ago • 0 comments

Checklist

  • [x] I have searched the issue list
  • [ ] I have tested my example against Shopify Liquid. (This isn't necessary if the actual behavior is a panic, or an error for which IsTemplateError returns false.)

Expected Behavior

This proposal originated from https://github.com/osteele/liquid/issues/35 but tries to tackle a broader issue, i.e.: Sometimes I might want to have more control over the rendering process, specifically where the renderer writes to.

Example

  • I might want to limit the number of bytes written when exposing templating to some untrusted party.
  • I might want to set a timeout for the rendering process.
  • I might want to render into a file directly.

Actual Behavior

The render package has a Render(node Node, w io.Writer, vars map[string]interface{}, c Config) function which takes an io.Writer, however, this is meant for internal use only. The Template methods have Render(vars Bindings) ([]byte, SourceError) and RenderString(b Bindings) (string, SourceError) which are useful in most circumstances but don't allow for more control as described above.

Detailed Description

Possible Solution

I'd propose a new Template render method FRender that allows to pass a custom io.Writer:

func (t *Template) FRender(w io.Writer, vars Bindings) SourceError

This gives more control over the rendering process when needed, e.g. you might want to set a render timeout to avoid deeple nested loop constructs in a template to cause potential server issues:

type CancelWriter struct {
	context.Context
	bytes.Buffer
}

func (w *CancelWriter) Write(bs []byte) (int, error) {
	if w.Err() != nil {
		return w.Len(), context.Cause(w)
	}
	return w.Buffer.Write(bs)
}

func Render() ([]byte, error) {
	wr := &CancelWriter{}
	if err := liquid.NewEngine().ParseAndFRender(wr, nil /*untrustworthy template*/, nil /*data*/); err != nil {
		return nil, err
	}
	return wr.Bytes(), nil
}

jamslinger avatar Apr 26 '24 09:04 jamslinger