init template
This commit is contained in:
38
engine/encode.go
Normal file
38
engine/encode.go
Normal 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
27
engine/encode_test.go
Normal 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
125
engine/engine.go
Normal 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
10
engine/suport.go
Normal 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
80
engine/template.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user