/
executor.go
109 lines (87 loc) · 2.81 KB
/
executor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package templates
import (
"bytes"
"context"
"io"
"net/http"
"slices"
"github.com/R-a-dio/valkyrie/errors"
"github.com/R-a-dio/valkyrie/util"
"github.com/R-a-dio/valkyrie/util/pool"
"go.opentelemetry.io/otel"
)
var bufferPool = pool.NewResetPool(func() *bytes.Buffer { return new(bytes.Buffer) })
type TemplateSelectable interface {
TemplateBundle() string
TemplateName() string
}
type Executor interface {
Execute(w io.Writer, r *http.Request, input TemplateSelectable) error
ExecuteTemplate(ctx context.Context, theme, page, template string, output io.Writer, input any) error
ExecuteAll(input TemplateSelectable) (map[string][]byte, error)
}
type executor struct {
site *Site
}
func newExecutor(site *Site) Executor {
return &executor{
site: site,
}
}
func (e *executor) Execute(w io.Writer, r *http.Request, input TemplateSelectable) error {
var ctx = r.Context()
theme := GetTheme(ctx)
// switch to a partial-page if we're asking for a full-page and it's htmx
templateName := input.TemplateName()
if util.IsHTMX(r) && templateName == "full-page" {
templateName = "partial-page"
}
return e.ExecuteTemplate(ctx, theme, input.TemplateBundle(), templateName, w, input)
}
// ExecuteTemplate selects a theme, page and template and feeds it the input given and writing the template output
// to the output writer. Output is buffered until template execution is done before writing to output.
func (e *executor) ExecuteTemplate(ctx context.Context, theme, page string, template string, output io.Writer, input any) error {
const op errors.Op = "templates/Executor.ExecuteTemplate"
// tracing support
ctx, span := otel.Tracer("templates").Start(ctx, "template")
defer span.End()
_, span = otel.Tracer("templates").Start(ctx, "template_load")
tmpl, err := e.site.Template(theme, page)
span.End()
if err != nil {
return errors.E(op, err)
}
b := bufferPool.Get()
defer bufferPool.Put(b)
_, span = otel.Tracer("templates").Start(ctx, "template_execute")
err = tmpl.ExecuteTemplate(b, template, input)
span.End()
if err != nil {
return errors.E(op, err)
}
_, err = io.Copy(output, b)
if err != nil {
return errors.E(op, err)
}
return nil
}
// ExecuteTemplateAll executes the template given feeding the input given for all known themes
func (e *executor) ExecuteAll(input TemplateSelectable) (map[string][]byte, error) {
const op errors.Op = "templates/Executor.ExecuteAll"
var out = make(map[string][]byte)
b := bufferPool.Get()
defer bufferPool.Put(b)
for _, theme := range e.site.ThemeNames() {
tmpl, err := e.site.Template(theme, input.TemplateBundle())
if err != nil {
return nil, errors.E(op, err)
}
err = tmpl.ExecuteTemplate(b, input.TemplateName(), input)
if err != nil {
return nil, errors.E(op, err)
}
out[theme] = slices.Clone(b.Bytes())
b.Reset()
}
return out, nil
}