refactor: new (simpler) version of the sl library
This commit is contained in:
@@ -5,69 +5,146 @@ import (
|
||||
"log"
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
initialized bool
|
||||
service Service
|
||||
type SlotKey[T any] *struct{}
|
||||
|
||||
func NewSlot[T any]() SlotKey[T] {
|
||||
return SlotKey[T](new(struct{}))
|
||||
}
|
||||
|
||||
type slot struct {
|
||||
createFunc func(*ServiceLocator) (any, error)
|
||||
created bool
|
||||
value any
|
||||
|
||||
typ string
|
||||
}
|
||||
|
||||
func (s *slot) checkInitialized(l *ServiceLocator) error {
|
||||
if !s.created {
|
||||
v, err := s.createFunc(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf(`initialized lazy value of type %T for slot of type %s`, v, s.typ)
|
||||
|
||||
s.value = v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ServiceLocator struct {
|
||||
providers map[string]*provider
|
||||
hooks map[string][]Service
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
Initialize(l *ServiceLocator) error
|
||||
providers map[any]*slot
|
||||
}
|
||||
|
||||
func New() *ServiceLocator {
|
||||
return &ServiceLocator{
|
||||
providers: map[string]*provider{},
|
||||
hooks: map[string][]Service{},
|
||||
providers: map[any]*slot{},
|
||||
}
|
||||
}
|
||||
|
||||
func getTypeName[T any]() string {
|
||||
var v T
|
||||
return fmt.Sprintf(`%T`, &v)[1:]
|
||||
}
|
||||
func InjectValue[T any](l *ServiceLocator, slotKey SlotKey[T], value T) T {
|
||||
log.Printf(`injected value of type %T for slot of type %s`, value, getTypeName[T]())
|
||||
|
||||
// Inject will set the implementation for "S" to "value" (the service will be initialized when needed after all of its dependencies)
|
||||
func Inject[S Service](l *ServiceLocator, value S) {
|
||||
key := getTypeName[S]()
|
||||
log.Printf(`injecting value of type %T for interface %s`, value, key)
|
||||
|
||||
l.providers[key] = &provider{false, value}
|
||||
}
|
||||
|
||||
// InjectValue will set the implementation for "S" to "value" and mark this service as already initialized (as this is just a constant)
|
||||
func InjectValue[S Service](l *ServiceLocator, value S) S {
|
||||
key := getTypeName[S]()
|
||||
log.Printf(`injecting value of type %T for interface %s`, value, key)
|
||||
|
||||
l.providers[key] = &provider{true, value}
|
||||
l.providers[slotKey] = &slot{nil, true, value, getTypeName[T]()}
|
||||
return value
|
||||
}
|
||||
|
||||
// Use will retrive from the service locator the implementation set for the type "T" and initialize the service if yet to be initialized
|
||||
func Use[T Service](l *ServiceLocator) (T, error) {
|
||||
provider, ok := l.providers[getTypeName[T]()]
|
||||
func InjectLazy[T any](l *ServiceLocator, slotKey SlotKey[T], createFunc func(*ServiceLocator) (T, error)) {
|
||||
log.Printf(`injected lazy for slot of type %s`, getTypeName[T]())
|
||||
|
||||
l.providers[slotKey] = &slot{
|
||||
createFunc: func(l *ServiceLocator) (any, error) {
|
||||
return createFunc(l)
|
||||
},
|
||||
created: false,
|
||||
value: nil,
|
||||
typ: getTypeName[T](),
|
||||
}
|
||||
}
|
||||
|
||||
func Use[T any](l *ServiceLocator, slotKey SlotKey[T]) (T, error) {
|
||||
var zero T
|
||||
|
||||
slot, ok := l.providers[slotKey]
|
||||
if !ok {
|
||||
var zero T
|
||||
return zero, fmt.Errorf(`no injected value for type "%T"`, zero)
|
||||
return zero, fmt.Errorf(`no injected value for type %s`, getTypeName[T]())
|
||||
}
|
||||
|
||||
if provider.initialized {
|
||||
service := provider.service.(T)
|
||||
return service, nil
|
||||
}
|
||||
|
||||
log.Printf(`initializing %T`, provider.service)
|
||||
if err := provider.service.Initialize(l); err != nil {
|
||||
var zero T
|
||||
err := slot.checkInitialized(l)
|
||||
if err != nil {
|
||||
return zero, err
|
||||
}
|
||||
|
||||
provider.initialized = true
|
||||
service := provider.service.(T)
|
||||
return service, nil
|
||||
v := slot.value.(T)
|
||||
|
||||
log.Printf(`using slot of type %s with value of type %T`, getTypeName[T](), v)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// // Require forces the initialization of a slot
|
||||
// func Require[T any](l *ServiceLocator, slotKey SlotKey[T]) error {
|
||||
// var zero T
|
||||
|
||||
// slot, ok := l.providers[slotKey]
|
||||
// if !ok {
|
||||
// return fmt.Errorf(`no injected value for type %T`, zero)
|
||||
// }
|
||||
|
||||
// return slot.checkInitialized(l)
|
||||
// }
|
||||
|
||||
// // MustRequire forces the initialization of a slot or panics if the slot is missing
|
||||
// func MustRequire[T any](l *ServiceLocator, slotKey SlotKey[T]) {
|
||||
// err := Require(l, slotKey)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Inject will set the implementation for "S" to "value" (the service will be initialized when needed after all of its dependencies)
|
||||
// func Inject[S Service](l *Context, value S) {
|
||||
// key := getTypeName[S]()
|
||||
// log.Printf(`injecting value of type %T for interface %s`, value, key)
|
||||
|
||||
// l.providers[key] = &provider{false, nil, value}
|
||||
// }
|
||||
|
||||
// // InjectValue will set the implementation for "S" to "value" and mark this service as already initialized (as this is just a constant)
|
||||
// func InjectValue[S Service](l *Context, value S) S {
|
||||
// key := getTypeName[S]()
|
||||
// log.Printf(`injecting value of type %T for interface %s`, value, key)
|
||||
|
||||
// l.providers[key] = &provider{true, nil, value}
|
||||
// return value
|
||||
// }
|
||||
|
||||
// // Use will retrive from the service locator the implementation set for the type "T" and initialize the service if yet to be initialized
|
||||
// func Use[T Service](l *Context) (T, error) {
|
||||
// provider, ok := l.providers[getTypeName[T]()]
|
||||
// if !ok {
|
||||
// var zero T
|
||||
// return zero, fmt.Errorf(`no injected value for type "%T"`, zero)
|
||||
// }
|
||||
|
||||
// if provider.initialized {
|
||||
// service := provider.service.(T)
|
||||
// return service, nil
|
||||
// }
|
||||
|
||||
// log.Printf(`initializing %T`, provider.service)
|
||||
// if err := provider.service.Initialize(l); err != nil {
|
||||
// var zero T
|
||||
// return zero, err
|
||||
// }
|
||||
|
||||
// provider.initialized = true
|
||||
// service := provider.service.(T)
|
||||
// return service, nil
|
||||
// }
|
||||
|
||||
func getTypeName[T any]() string {
|
||||
var zero T
|
||||
return fmt.Sprintf(`%T`, &zero)[1:]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user