package render import ( "context" "errors" "fmt" "io" ) var ErrNotFound = errors.New("not found") func New(parser Parser, engines ...Engine) *Render { return &Render{ parser: parser, engines: engines, } } type Render struct { engines []Engine parser Parser } type Execute func(ctx context.Context, wr io.Writer, data interface{}, params Params) error type Engine interface { Support(context.Context, Reference) bool Load(ctx context.Context, reference Reference) (Execute, error) } type Parser func(ctx context.Context, name string, opts ...Option) (Reference, error) // Add add engine. This function is not thread-safe. func (e *Render) Add(engine ...Engine) { e.engines = append(e.engines, engine...) } func (e *Render) WithParser(parser Parser) *Render { return New(parser, e.engines...) } func (e *Render) Load(ctx context.Context, reference Reference) (Execute, error) { for _, engine := range e.engines { if engine.Support(ctx, reference) { exec, err := engine.Load(ctx, reference) if err != nil { return nil, fmt.Errorf("render load:%w", err) } return exec, nil } } return nil, fmt.Errorf("loader:%w", ErrNotFound) } func (e *Render) Parse(ctx context.Context, name string, opts ...Option) (Reference, error) { return e.parser(ctx, name, opts...) } func (e *Render) Execute(ctx context.Context, wr io.Writer, name string, data interface{}, opts ...Option) error { reference, rerr := e.parser(ctx, name, opts...) if rerr != nil { return fmt.Errorf("parse: %w", rerr) } execute, err := e.Load(ctx, reference) if err != nil { return fmt.Errorf("engine load:%w", err) } if err := execute(ctx, wr, data, reference.Params); err != nil { return fmt.Errorf("render execute:%w", err) } return nil }