2021-09-29 20:38:50 +05:00

103 lines
2.2 KiB
Go

package tpl
import (
"errors"
"sync"
)
// Annotator allows to annotate error code.
type Annotator struct {
Message string
Description string
}
// Errors is a "cached storage" for the rendered error pages for the different templates and codes.
type Errors struct {
templates map[string][]byte
codes map[string]Annotator
cacheMu sync.RWMutex
cache map[string]map[string][]byte // map[template]map[code]content
}
// NewErrors creates new Errors.
func NewErrors(templates map[string][]byte, codes map[string]Annotator) *Errors {
return &Errors{
templates: templates,
codes: codes,
cache: make(map[string]map[string][]byte),
}
}
func (e *Errors) existsInCache(template, code string) ([]byte, bool) {
e.cacheMu.RLock()
defer e.cacheMu.RUnlock()
if codes, tplOk := e.cache[template]; tplOk {
if content, codeOk := codes[code]; codeOk {
return content, true
}
}
return nil, false
}
func (e *Errors) putInCache(template, code string) error {
if _, ok := e.templates[template]; !ok {
return errors.New("template \"" + template + "\" does not exists")
}
if _, ok := e.codes[code]; !ok {
return errors.New("code \"" + code + "\" does not exists")
}
e.cacheMu.Lock()
defer e.cacheMu.Unlock()
if _, ok := e.cache[template]; !ok {
e.cache[template] = make(map[string][]byte)
}
e.cache[template][code] = Replace(e.templates[template], Replaces{
Code: code,
Message: e.codes[code].Message,
Description: e.codes[code].Description,
})
return nil
}
// Get the rendered error page content.
func (e *Errors) Get(template, code string) ([]byte, error) {
if content, ok := e.existsInCache(template, code); ok {
return content, nil
}
if err := e.putInCache(template, code); err != nil {
return nil, err
}
e.cacheMu.RLock()
defer e.cacheMu.RUnlock()
return e.cache[template][code], nil
}
// VisitAll allows to iterate all possible error pages and templates.
func (e *Errors) VisitAll(fn func(template, code string, content []byte) error) error {
for tpl := range e.templates {
for code := range e.codes {
content, err := e.Get(tpl, code)
if err != nil {
return err // will never happen
}
if err = fn(tpl, code, content); err != nil {
return err
}
}
}
return nil
}