package env import ( "bytes" "io" "strings" "github.com/pkg/errors" // "github.com/noirbizarre/gonja/config" "github.com/noirbizarre/gonja/eval" "github.com/noirbizarre/gonja/nodes" "github.com/noirbizarre/gonja/parser" "github.com/noirbizarre/gonja/tokens" ) type TemplateGetter interface { GetTemplate(string) (*Template, error) } // type TemplateWriter interface { // io.Writer // WriteString(string) (int, error) // } // type templateWriter struct { // w io.Writer // } // func (tw *templateWriter) WriteString(s string) (int, error) { // return tw.w.Write([]byte(s)) // } // func (tw *templateWriter) Write(b []byte) (int, error) { // return tw.w.Write(b) // } type Template struct { // set *TemplateSet Env *Environment // Input // isTplString bool Name string Reader io.Reader Source string // Config *config.Config // size int // Calculation // tokens []*Token Tokens *tokens.Stream Parser *parser.Parser Root *nodes.Template Parent *Template Blocks map[string]*nodes.Wrapper Macros eval.MacroSet // first come, first serve (it's important to not override existing entries in here) // level int // parent *Template // child *Template // blocks map[string]*NodeWrapper // exportedMacros map[string]*MacroStmt // // Output // root *nodeDocument } // func newTemplateString(env *Environment, tpl []byte) (*Template, error) { // return newTemplate(env, "", true, tpl) // } func NewTemplate(name string, source string, env *Environment) (*Template, error) { // source := string(tpl) // if cfg == nil { // cfg = config.DefaultConfig // } // Create the template t := &Template{ Env: env, // isTplString: isTplString, Name: name, Source: source, Tokens: tokens.Lex(source), // Config: cfg, // size: len(strTpl), // blocks: make(map[string]*NodeWrapper), // exportedMacros: make(map[string]*MacroStmt), } // // Tokenize it // tokens, err := lex(name, strTpl) // if err != nil { // return nil, err // } // t.tokens = tokens // For debugging purposes, show all tokens: /*for i, t := range tokens { fmt.Printf("%3d. %s\n", i, t) }*/ // Parse it t.Parser = parser.NewParser(name, env.Config, t.Tokens) t.Parser.Statements = t.Env.Statements //.Parsers() root, err := t.Parser.Parse() if err != nil { return nil, err } t.Root = root return t, nil } func (tpl *Template) execute(ctx eval.Context, out io.StringWriter) error { // Determine the parent to be executed (for template inheritance) parent := tpl for parent.Parent != nil { parent = parent.Parent } // Create context if none is given newCtx := eval.Context{} // newContext := make(Context) // newContext.Update(tpl.set.Globals) if ctx != nil { newCtx.Update(ctx) if len(newCtx) > 0 { // // Check for context name syntax // err := newCtx.checkForValidIdentifiers() // if err != nil { // return err // } // Check for clashes with macro names for k := range newCtx { _, has := tpl.Macros[k] if has { return errors.Errorf("context key name '%s' clashes with macro '%s'", k, k) } } } } // Create operational context exCtx := eval.NewExecutionContext(newCtx) exCtx.Globals.Update(tpl.Env.Globals) // exCtx := newExecutionContext(parent, newCtx) var builder strings.Builder renderer := eval.NewRenderer(exCtx, &builder, tpl.Env.EvalConfig) nodes.Walk(renderer, tpl.Root) // nodes.Inspect(tpl.Root, renderer.Render) // // Run the selected document // if err := parent.Root.Execute(exCtx, writer); err != nil { // return err // } out.WriteString(builder.String()) return nil } // func (tpl *Template) newTemplateWriterAndExecute(ctx *Context, writer io.Writer) error { // return tpl.execute(ctx, &templateWriter{w: writer}) // } func (tpl *Template) newBufferAndExecute(ctx eval.Context) (*bytes.Buffer, error) { var buffer bytes.Buffer // Create output buffer // We assume that the rendered template will be 30% larger // buffer := bytes.NewBuffer(make([]byte, 0, int(float64(tpl.size)*1.3))) if err := tpl.execute(ctx, &buffer); err != nil { return nil, err } return &buffer, nil } // // Executes the template with the given context and writes to writer (io.Writer) // // on success. Context can be nil. Nothing is written on error; instead the error // // is being returned. // func (tpl *Template) ExecuteWriter(ctx *Context, writer io.Writer) error { // buf, err := tpl.newBufferAndExecute(ctx) // if err != nil { // return err // } // _, err = buf.WriteTo(writer) // if err != nil { // return err // } // return nil // } // // // Same as ExecuteWriter. The only difference between both functions is that // // // this function might already have written parts of the generated template in the // // // case of an execution error because there's no intermediate buffer involved for // // // performance reasons. This is handy if you need high performance template // // // generation or if you want to manage your own pool of buffers. // // func (tpl *Template) ExecuteWriterUnbuffered(ctx *Context, writer io.Writer) error { // // return tpl.newTemplateWriterAndExecute(ctx, writer) // // } // Executes the template and returns the rendered template as a []byte func (tpl *Template) ExecuteBytes(ctx map[string]interface{}) ([]byte, error) { // Execute template buffer, err := tpl.newBufferAndExecute(ctx) if err != nil { return nil, err } return buffer.Bytes(), nil } // Executes the template and returns the rendered template as a string func (tpl *Template) Execute(ctx map[string]interface{}) (string, error) { var b strings.Builder err := tpl.execute(ctx, &b) // Execute template // buffer, err := tpl.newBufferAndExecute(ctx) if err != nil { return "", err } return b.String(), nil }