geezer/kernel.go
Alexander Kiryukhin d22326d5b8
Initial
2022-02-06 22:33:02 +03:00

163 lines
4.5 KiB
Go

package geezer
import (
"errors"
"strings"
"github.com/neonxp/geezer/render"
)
var (
ErrServiceNotFound = errors.New("service not found")
ErrMethodNotFound = errors.New("method not found")
)
type defaultKernel struct {
routes map[string]Service
hooks map[string]map[HookLifecycle]map[HookType][]Hook
}
func newKernel() *defaultKernel {
return &defaultKernel{
routes: map[string]Service{},
hooks: map[string]map[HookLifecycle]map[HookType][]Hook{},
}
}
func (s *defaultKernel) Register(name string, service Service) error {
name = strings.ToLower(name)
s.routes[name] = service
if _, exist := s.hooks[name]; !exist {
s.hooks[name] = map[HookLifecycle]map[HookType][]Hook{}
}
if err := service.Setup(s, name); err != nil {
return err
}
return nil
}
func (s *defaultKernel) Hook(service string, lifecycle HookLifecycle, hookType HookType, hook Hook) {
service = strings.ToLower(service)
if _, exist := s.hooks[service]; !exist {
s.hooks[service] = map[HookLifecycle]map[HookType][]Hook{}
}
if _, exist := s.hooks[service][lifecycle]; !exist {
s.hooks[service][lifecycle] = map[HookType][]Hook{}
}
if _, exist := s.hooks[service][lifecycle][hookType]; !exist {
s.hooks[service][lifecycle][hookType] = []Hook{}
}
s.hooks[service][lifecycle][hookType] = append(s.hooks[service][lifecycle][hookType], hook)
}
func (s *defaultKernel) Service(name string) Service {
if service, exist := s.routes[name]; exist {
return service
}
return nil
}
func (s *defaultKernel) Call(method Method, name, id string, data Data, params Params) (render.Renderer, error) {
name = strings.ToLower(name)
service := s.Service(name)
if service == nil {
return nil, ErrServiceNotFound
}
hookCtx, result, err := s.callBeforeHooks(method, name, id, data, params)
if err != nil {
return result, err
}
switch hookCtx.Method {
case MethodFind:
result, err = service.Find(hookCtx.Params)
case MethodGet:
result, err = service.Get(hookCtx.ID, hookCtx.Params)
case MethodCreate:
result, err = service.Create(hookCtx.Data, hookCtx.Params)
case MethodUpdate:
result, err = service.Update(hookCtx.ID, hookCtx.Data, hookCtx.Params)
case MethodPatch:
result, err = service.Patch(hookCtx.ID, hookCtx.Data, hookCtx.Params)
case MethodRemove:
err = service.Remove(hookCtx.ID, hookCtx.Params)
default:
return nil, ErrMethodNotFound
}
return s.callAfterHooks(method, name, hookCtx, result, err)
}
func (s *defaultKernel) callBeforeHooks(method Method, name string, id string, data Data, params Params) (*HookContext, render.Renderer, error) {
var beforeHooks []Hook
if hooks, ok := s.hooks[name][HookBefore]; ok {
if allHooks, ok := hooks[HookAll]; ok {
beforeHooks = append(beforeHooks, allHooks...)
}
if methodHooks, ok := hooks[hookTypeFromMethod[method]]; ok {
beforeHooks = append(beforeHooks, methodHooks...)
}
}
hookCtx := &HookContext{
App: s,
Method: method,
Type: HookBefore,
ID: id,
Params: params,
Data: data,
Err: nil,
Result: nil,
StatusCode: 0,
}
for _, hook := range beforeHooks {
if err := hook(hookCtx); err != nil {
return nil, nil, err
}
}
return hookCtx, nil, nil
}
func (s *defaultKernel) callAfterHooks(method Method, name string, hookCtx *HookContext, result render.Renderer, err error) (render.Renderer, error) {
var afterHooks []Hook
if hooks, ok := s.hooks[name][HookAfter]; ok {
if allHooks, ok := hooks[HookAll]; ok {
afterHooks = append(afterHooks, allHooks...)
}
if methodHooks, ok := hooks[hookTypeFromMethod[method]]; ok {
afterHooks = append(afterHooks, methodHooks...)
}
}
var errorHooks []Hook
if hooks, ok := s.hooks[name][HookError]; ok {
if allHooks, ok := hooks[HookAll]; ok {
errorHooks = append(errorHooks, allHooks...)
}
if methodHooks, ok := hooks[hookTypeFromMethod[method]]; ok {
errorHooks = append(errorHooks, methodHooks...)
}
}
hookCtx.Result = result
hookCtx.Err = err
if err != nil {
for _, hook := range errorHooks {
if err := hook(hookCtx); err != nil {
return nil, err
}
}
return hookCtx.Result, hookCtx.Err
}
for _, hook := range afterHooks {
if err := hook(hookCtx); err != nil {
return nil, err
}
}
return hookCtx.Result, hookCtx.Err
}
type Kernel interface {
Register(name string, service Service) error
Hook(service string, lifecycle HookLifecycle, hookType HookType, hook Hook)
Service(name string) Service
Call(method Method, name, id string, data Data, params Params) (render.Renderer, error)
}