96 lines
1.9 KiB
Go
96 lines
1.9 KiB
Go
package service
|
|
|
|
import (
|
|
"log"
|
|
"reflect"
|
|
"sync"
|
|
)
|
|
|
|
type Lifecycle = string
|
|
|
|
var (
|
|
Singleton Lifecycle = "singleton"
|
|
Transient Lifecycle = "transient"
|
|
)
|
|
|
|
type Provider struct {
|
|
cb func() any
|
|
lifecycle Lifecycle
|
|
}
|
|
|
|
type Locator struct {
|
|
services map[string]Provider
|
|
cache map[string]any
|
|
mutex sync.RWMutex
|
|
}
|
|
|
|
// NewLocator creates a new locator to register services
|
|
// Usage:
|
|
//
|
|
// locator := service.NewLocator()
|
|
// service.Set[db.Queries](locator, service.Singleton, db.Provide)
|
|
//
|
|
// service.Get[db.Queries](locator)
|
|
func NewLocator() *Locator {
|
|
return &Locator{
|
|
services: make(map[string]Provider),
|
|
cache: make(map[string]any),
|
|
mutex: sync.RWMutex{},
|
|
}
|
|
}
|
|
|
|
func (l *Locator) setCache(key string, value any) {
|
|
l.cache[key] = value
|
|
}
|
|
|
|
func (l *Locator) clearCache(key string) {
|
|
delete(l.cache, key)
|
|
}
|
|
|
|
func (l *Locator) getCache(key string) any {
|
|
return l.cache[key]
|
|
}
|
|
|
|
// Get returns a service from the locator
|
|
// If the service is not found, log.Fatalf is called
|
|
// If the service is a singleton, it will be cached after first invocation
|
|
func Get[T any](locator *Locator) *T {
|
|
locator.mutex.RLock()
|
|
i := new(T)
|
|
t := reflect.TypeOf(i).String()
|
|
|
|
cached := locator.getCache(t)
|
|
|
|
if cached != nil {
|
|
locator.mutex.RUnlock()
|
|
return cached.(*T)
|
|
}
|
|
|
|
entry, ok := locator.services[t]
|
|
if !ok {
|
|
log.Fatalf("%s is not registered in the service locator", t)
|
|
}
|
|
cb := entry.cb().(*T)
|
|
locator.mutex.RUnlock()
|
|
locator.mutex.Lock()
|
|
if entry.lifecycle == Singleton {
|
|
locator.setCache(t, cb)
|
|
}
|
|
locator.mutex.Unlock()
|
|
return cb
|
|
}
|
|
|
|
// Set registers a service with the locator
|
|
// If the service is a singleton, it will be cached after first invocation of Get
|
|
func Set[T any](locator *Locator, lifecycle Lifecycle, value func() *T) {
|
|
t := reflect.TypeOf(value)
|
|
rt := t.Out(0)
|
|
key := rt.String()
|
|
locator.services[key] = Provider{
|
|
cb: func() any {
|
|
return value()
|
|
},
|
|
lifecycle: lifecycle,
|
|
}
|
|
locator.clearCache(key)
|
|
}
|