|
|
|
package sl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
)
|
|
|
|
|
|
|
|
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.created = true
|
|
|
|
s.value = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type ServiceLocator struct {
|
|
|
|
providers map[any]*slot
|
|
|
|
}
|
|
|
|
|
|
|
|
func New() *ServiceLocator {
|
|
|
|
return &ServiceLocator{
|
|
|
|
providers: map[any]*slot{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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]())
|
|
|
|
|
|
|
|
l.providers[slotKey] = &slot{
|
|
|
|
nil,
|
|
|
|
true,
|
|
|
|
value,
|
|
|
|
getTypeName[T](),
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
|
|
|
return zero, fmt.Errorf(`no injected value for type %s`, getTypeName[T]())
|
|
|
|
}
|
|
|
|
|
|
|
|
err := slot.checkInitialized(l)
|
|
|
|
if err != nil {
|
|
|
|
return zero, err
|
|
|
|
}
|
|
|
|
|
|
|
|
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:]
|
|
|
|
}
|