You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
website/sl/sl.go

157 lines
3.6 KiB
Go

2 years ago
package sl
import (
"fmt"
"log"
)
type SlotKey[T any] *struct{}
2 years ago
func NewSlot[T any]() SlotKey[T] {
return SlotKey[T](new(struct{}))
2 years ago
}
type slot struct {
createFunc func(*ServiceLocator) (any, error)
created bool
value any
typ string
2 years ago
}
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
2 years ago
}
return nil
2 years ago
}
type ServiceLocator struct {
providers map[any]*slot
}
2 years ago
func New() *ServiceLocator {
return &ServiceLocator{
providers: map[any]*slot{},
}
2 years ago
}
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]())
2 years ago
l.providers[slotKey] = &slot{
nil,
true,
value,
getTypeName[T](),
}
2 years ago
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](),
2 years ago
}
}
func Use[T any](l *ServiceLocator, slotKey SlotKey[T]) (T, error) {
var zero T
2 years ago
slot, ok := l.providers[slotKey]
if !ok {
return zero, fmt.Errorf(`no injected value for type %s`, getTypeName[T]())
2 years ago
}
err := slot.checkInitialized(l)
if err != nil {
2 years ago
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:]
2 years ago
}