init template

This commit is contained in:
andrey1s
2022-10-02 23:31:19 +03:00
parent 3d5d51b2df
commit 621afcc59c
24 changed files with 969 additions and 0 deletions

38
engine/encode.go Normal file
View File

@@ -0,0 +1,38 @@
package engine
import (
"context"
"fmt"
"io"
"gitoa.ru/go-4devs/mime"
"gitoa.ru/go-4devs/templating/render"
)
func NewEncode(name string, encode func(w io.Writer, data interface{}) error, ext ...mime.Ext) Encode {
return Encode{
name: name,
encode: encode,
formats: ext,
}
}
type Encode struct {
encode func(w io.Writer, data interface{}) error
name string
formats []mime.Ext
}
func (e Encode) Support(_ context.Context, reference render.Reference) bool {
return Support(reference, e.name, e.formats...)
}
func (e Encode) Load(context.Context, render.Reference) (render.Execute, error) {
return func(_ context.Context, wr io.Writer, data interface{}, _ render.Params) error {
if err := e.encode(wr, data); err != nil {
return fmt.Errorf("%s engine:%w", e.name, err)
}
return nil
}, nil
}

27
engine/encode_test.go Normal file
View File

@@ -0,0 +1,27 @@
package engine_test
import (
"bytes"
"context"
"encoding/json"
"io"
"testing"
"github.com/stretchr/testify/require"
"gitoa.ru/go-4devs/templating/engine"
"gitoa.ru/go-4devs/templating/render"
)
func TestEncodeLoad(t *testing.T) {
t.Parallel()
ctx := context.Background()
buff := bytes.Buffer{}
exec, err := engine.NewEncode("json", func(w io.Writer, v interface{}) error {
return json.NewEncoder(w).Encode(v)
}).Load(ctx, render.NewReference("any"))
require.NoError(t, err)
require.NoError(t, exec(ctx, &buff, map[string]string{"name": "json data"}, nil))
require.Equal(t, "{\"name\":\"json data\"}\n", buff.String())
}

125
engine/engine.go Normal file
View File

@@ -0,0 +1,125 @@
package engine
import (
"context"
"errors"
"fmt"
"gitoa.ru/go-4devs/mime"
"gitoa.ru/go-4devs/templating/loader"
"gitoa.ru/go-4devs/templating/render"
)
var _ render.Engine = (*Engine)(nil)
var (
ErrNotSupport = errors.New("not support")
ErrDuplicate = errors.New("duplicate")
ErrNotFound = errors.New("not found")
)
type (
Option func(*Engine)
Parse func(loader.Source) (Template, error)
)
type Cache interface {
Set(ctx context.Context, tpl Template) error
Get(ctx context.Context, name string) (Template, error)
List(_ context.Context) []Template
}
func WithTemplates(tpls ...Template) Option {
ctx := context.Background()
return func(l *Engine) {
for _, tpl := range tpls {
_ = l.tpls.Set(ctx, tpl)
}
}
}
func WithLoader(load loader.Loader) Option {
return func(l *Engine) {
l.load = load
}
}
func WithFormats(formats ...mime.Ext) Option {
return func(e *Engine) {
e.formats = formats
}
}
func New(name string, parse Parse, opts ...Option) *Engine {
engine := Engine{
name: name,
parce: parse,
tpls: NewTemplates(),
load: loader.Empty(),
}
for _, opt := range opts {
opt(&engine)
}
return &engine
}
type Engine struct {
name string
formats []mime.Ext
tpls Cache
load loader.Loader
parce Parse
}
func (l Engine) WithLoader(load loader.Loader) *Engine {
return New(l.name, l.parce, WithTemplates(l.tpls.List(context.Background())...), WithLoader(load))
}
func (l Engine) Add(ctx context.Context, tpls ...Template) error {
for idx, tpl := range tpls {
if err := l.tpls.Set(ctx, tpl); err != nil {
return fmt.Errorf("engine add template[%d] with name %s: %w", idx, tpl.Name(), err)
}
}
return nil
}
func (l Engine) Support(ctx context.Context, tpl render.Reference) bool {
return Support(tpl, l.name, l.formats...)
}
func (l Engine) Load(ctx context.Context, reference render.Reference) (render.Execute, error) {
var (
tpl Template
err error
)
tpl, err = l.tpls.Get(ctx, reference.Name())
if err == nil {
return tpl.Execute, nil
}
if !errors.Is(err, ErrNotFound) {
return nil, fmt.Errorf("load get:%w", err)
}
source, err := l.load.Load(ctx, reference)
if err != nil {
return nil, fmt.Errorf("load source:%w", err)
}
tpl, err = l.parce(source)
if err != nil {
return nil, fmt.Errorf("load parce:%w", err)
}
if err := l.tpls.Set(ctx, tpl); err != nil {
return nil, fmt.Errorf("load set:%w", err)
}
return tpl.Execute, nil
}

10
engine/suport.go Normal file
View File

@@ -0,0 +1,10 @@
package engine
import (
"gitoa.ru/go-4devs/mime"
"gitoa.ru/go-4devs/templating/render"
)
func Support(reference render.Reference, name string, formats ...mime.Ext) bool {
return (reference.Engine != "" && reference.IsEngine(name)) || (len(formats) != 0 && reference.IsFromat(formats...))
}

80
engine/template.go Normal file
View File

@@ -0,0 +1,80 @@
package engine
import (
"context"
"fmt"
"io"
"sync"
"gitoa.ru/go-4devs/templating/render"
)
type Template interface {
Name() string
Execute(ctx context.Context, w io.Writer, data interface{}, param render.Params) error
}
func NewTemplates() Templates {
return Templates{
list: make(map[string]Template),
mu: &sync.RWMutex{},
}
}
type Templates struct {
list map[string]Template
mu *sync.RWMutex
}
func (t Templates) Get(_ context.Context, name string) (Template, error) {
t.mu.RLock()
defer t.mu.RUnlock()
if tpl, ok := t.list[name]; ok {
return tpl, nil
}
return nil, fmt.Errorf("templates get:%w", ErrNotFound)
}
func (t Templates) Set(_ context.Context, tpl Template) error {
t.mu.Lock()
defer t.mu.Unlock()
if _, ok := t.list[tpl.Name()]; ok {
return fmt.Errorf("templates set:%w", ErrDuplicate)
}
t.list[tpl.Name()] = tpl
return nil
}
func (t Templates) List(_ context.Context) []Template {
list := make([]Template, 0, len(t.list))
for _, tpl := range t.list {
list = append(list, tpl)
}
return list
}
func NewTemplate(name string, execute func(w io.Writer, data interface{}) error) ExecTemplate {
return ExecTemplate{
name: name,
execute: execute,
}
}
type ExecTemplate struct {
name string
execute func(w io.Writer, data interface{}) error
}
func (t ExecTemplate) Name() string {
return t.name
}
func (t ExecTemplate) Execute(_ context.Context, w io.Writer, data interface{}, _ render.Params) error {
return t.execute(w, data)
}