139 lines
4.4 KiB
Go
139 lines
4.4 KiB
Go
package templates
|
|
|
|
import (
|
|
"html/template"
|
|
"net/http"
|
|
|
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
|
)
|
|
|
|
// Parsed represents a parsed template that receives an underlying context of type C
|
|
type Parsed[C any] struct {
|
|
template *template.Template
|
|
}
|
|
|
|
// Parse creates a new Parsed from a template source.
|
|
// Parse calls panic() when parsing fails.
|
|
func Parse[C any](name string, source []byte, Assets assets.Assets) Parsed[C] {
|
|
return Parsed[C]{
|
|
template: Assets.MustParseShared(name, string(source)),
|
|
}
|
|
}
|
|
|
|
// Prepare prepares this template for use inside a concrete handler.
|
|
// gaps must either be of length 0 or length 1 and may pre-fill gaps to be used when executing the template later.
|
|
func (p *Parsed[C]) Prepare(tpl *Templating, gaps ...BaseContextGaps) *Template[C] {
|
|
wrap := Template[C]{
|
|
tpl: tpl,
|
|
template: tpl.Template(p.template),
|
|
}
|
|
if len(gaps) > 1 {
|
|
panic("WrapTemplate: must provide either 1 or no gaps")
|
|
}
|
|
if len(gaps) == 1 {
|
|
wrap.gaps = gaps[0]
|
|
}
|
|
return &wrap
|
|
}
|
|
|
|
// Tempalte represents an executable template.
|
|
type Template[C any] struct {
|
|
tpl *Templating
|
|
template *template.Template
|
|
gaps BaseContextGaps
|
|
}
|
|
|
|
// Template returns a template that, if executed together with the context by the Context method, produces the desired result.
|
|
func (tw *Template[C]) Template() *template.Template {
|
|
return tw.template
|
|
}
|
|
|
|
// Context generates a context for a given request that can be used to execute the provided template.
|
|
func (tw *Template[C]) Context(r *http.Request, c C, gaps ...BaseContextGaps) any {
|
|
// make the gaps something
|
|
if len(gaps) > 1 {
|
|
panic("Context: must provide either 1 or no gaps")
|
|
}
|
|
|
|
// update the context with gaps
|
|
{
|
|
g := tw.gaps
|
|
if len(gaps) == 1 {
|
|
g = gaps[0]
|
|
}
|
|
tw.tpl.update(&c, r, g)
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
// ParseForm is like Parse[BaseFormContext]
|
|
var ParseForm = Parse[BaseFormContext]
|
|
|
|
// FormTemplateContext returns a new handler for a form with the given base context
|
|
func FormTemplateContext(tw *Template[BaseFormContext]) func(ctx httpx.FormContext, r *http.Request) any {
|
|
return func(ctx httpx.FormContext, r *http.Request) any {
|
|
return tw.Context(r, BaseFormContext{FormContext: ctx})
|
|
}
|
|
}
|
|
|
|
// MappedHandler returns a new handler that maps the incoming context via f
|
|
func MappedHandler[In, Out any](tw *Template[Out], f func(ctx In, r *http.Request) (Out, BaseContextGaps)) func(ctx In, r *http.Request) any {
|
|
// TODO: Should this one be removed?
|
|
return func(ctx In, r *http.Request) any {
|
|
c, g := f(ctx, r)
|
|
return tw.Context(r, c, g)
|
|
}
|
|
}
|
|
|
|
// Hander returns a function that returns a context for the given template
|
|
func (tw *Template[C]) Handler(f func(r *http.Request) (C, error)) func(r *http.Request) (any, error) {
|
|
// TODO: Should this one be removed?
|
|
return tw.HandlerWithGaps(func(r *http.Request, gaps *BaseContextGaps) (C, error) {
|
|
return f(r)
|
|
})
|
|
}
|
|
|
|
// HTMLHandler returns a new HTMLHandler for this request
|
|
func (tw *Template[C]) HTMLHandler(f func(r *http.Request) (C, error)) httpx.HTMLHandler[any] {
|
|
return httpx.HTMLHandler[any]{
|
|
Handler: tw.Handler(f),
|
|
Template: tw.Template(),
|
|
}
|
|
}
|
|
|
|
// HandlerWithGaps works like handler, but additionally receives a gaps object to update.
|
|
func (tw *Template[C]) HandlerWithGaps(f func(r *http.Request, gaps *BaseContextGaps) (C, error)) func(r *http.Request) (any, error) {
|
|
// TODO: Drop this variant?
|
|
var zero C
|
|
return func(r *http.Request) (any, error) {
|
|
g := tw.gaps.clone()
|
|
c, err := f(r, &g)
|
|
if err != nil {
|
|
return zero, err
|
|
}
|
|
|
|
// update the context
|
|
return tw.Context(r, c, g), nil
|
|
}
|
|
}
|
|
|
|
func (tw *Template[C]) HTMLHandlerWithGaps(f func(r *http.Request, gaps *BaseContextGaps) (C, error)) httpx.HTMLHandler[any] {
|
|
return httpx.HTMLHandler[any]{
|
|
Handler: tw.HandlerWithGaps(f),
|
|
Template: tw.Template(),
|
|
}
|
|
}
|
|
|
|
// Execute executes this template with the given context
|
|
func (tw *Template[C]) Execute(w http.ResponseWriter, r *http.Request, c C, gaps ...BaseContextGaps) error {
|
|
return tw.ExecuteWithError(w, r, c, nil, gaps...)
|
|
}
|
|
|
|
// ExecuteWithError executes this template, or the default error handler if err != nil
|
|
func (tw *Template[C]) ExecuteWithError(w http.ResponseWriter, r *http.Request, c C, err error, gaps ...BaseContextGaps) error {
|
|
// TODO: Drop this variant?
|
|
// TODO: This should be removed!
|
|
return httpx.WriteHTML(tw.Context(r, c, gaps...), err, tw.template, "", w, r)
|
|
}
|