fix(registry): log Unregister Close errors, add doc comments and 59 unit tests
Log close errors in Unregister instead of silently discarding them. Add doc comments to all exported symbols across 20 registry files. Add comprehensive tests covering base registry, all wrapper types, hot-reload delegation, nil-fallback semantics, and global accessors.
This commit is contained in:
@@ -6,14 +6,19 @@ import (
|
||||
"github.com/go-gost/core/admission"
|
||||
)
|
||||
|
||||
// admissionRegistry implements a hot-reload-safe registry for admission.Admission.
|
||||
// The Get method returns a wrapper that looks up the current value on every call.
|
||||
type admissionRegistry struct {
|
||||
registry[admission.Admission]
|
||||
}
|
||||
|
||||
// Register stores an Admission under the given name.
|
||||
func (r *admissionRegistry) Register(name string, v admission.Admission) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered Admission.
|
||||
// Returns nil if name is empty.
|
||||
func (r *admissionRegistry) Get(name string) admission.Admission {
|
||||
if name != "" {
|
||||
return &admissionWrapper{name: name, r: r}
|
||||
|
||||
@@ -6,14 +6,18 @@ import (
|
||||
"github.com/go-gost/core/auth"
|
||||
)
|
||||
|
||||
// autherRegistry implements a hot-reload-safe registry for auth.Authenticator.
|
||||
type autherRegistry struct {
|
||||
registry[auth.Authenticator]
|
||||
}
|
||||
|
||||
// Register stores an Authenticator under the given name.
|
||||
func (r *autherRegistry) Register(name string, v auth.Authenticator) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered Authenticator.
|
||||
// Returns nil if name is empty.
|
||||
func (r *autherRegistry) Get(name string) auth.Authenticator {
|
||||
if name != "" {
|
||||
return &autherWrapper{name: name, r: r}
|
||||
|
||||
@@ -6,14 +6,18 @@ import (
|
||||
"github.com/go-gost/core/bypass"
|
||||
)
|
||||
|
||||
// bypassRegistry implements a hot-reload-safe registry for bypass.Bypass.
|
||||
type bypassRegistry struct {
|
||||
registry[bypass.Bypass]
|
||||
}
|
||||
|
||||
// Register stores a Bypass under the given name.
|
||||
func (r *bypassRegistry) Register(name string, v bypass.Bypass) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered Bypass.
|
||||
// Returns nil if name is empty.
|
||||
func (r *bypassRegistry) Get(name string) bypass.Bypass {
|
||||
if name != "" {
|
||||
return &bypassWrapper{name: name, r: r}
|
||||
|
||||
@@ -8,14 +8,18 @@ import (
|
||||
"github.com/go-gost/core/selector"
|
||||
)
|
||||
|
||||
// chainRegistry implements a hot-reload-safe registry for chain.Chainer.
|
||||
type chainRegistry struct {
|
||||
registry[chain.Chainer]
|
||||
}
|
||||
|
||||
// Register stores a Chainer under the given name.
|
||||
func (r *chainRegistry) Register(name string, v chain.Chainer) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered Chainer.
|
||||
// Returns nil if name is empty.
|
||||
func (r *chainRegistry) Get(name string) chain.Chainer {
|
||||
if name != "" {
|
||||
return &chainWrapper{name: name, r: r}
|
||||
|
||||
@@ -5,12 +5,18 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
// NewConnector is a factory function that creates a new connector.Connector
|
||||
// from the given options.
|
||||
type NewConnector func(opts ...connector.Option) connector.Connector
|
||||
|
||||
// connectorRegistry stores connector factory functions. Duplicate registrations
|
||||
// are treated as fatal errors (logger.Default().Fatal).
|
||||
type connectorRegistry struct {
|
||||
registry[NewConnector]
|
||||
}
|
||||
|
||||
// Register stores a connector factory under the given name. Calls Fatal on
|
||||
// duplicate registration since connector names must be unique at init time.
|
||||
func (r *connectorRegistry) Register(name string, v NewConnector) error {
|
||||
if err := r.registry.Register(name, v); err != nil {
|
||||
logger.Default().Fatal(err)
|
||||
|
||||
@@ -5,12 +5,18 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
// NewDialer is a factory function that creates a new dialer.Dialer
|
||||
// from the given options.
|
||||
type NewDialer func(opts ...dialer.Option) dialer.Dialer
|
||||
|
||||
// dialerRegistry stores dialer factory functions. Duplicate registrations
|
||||
// are treated as fatal errors (logger.Default().Fatal).
|
||||
type dialerRegistry struct {
|
||||
registry[NewDialer]
|
||||
}
|
||||
|
||||
// Register stores a dialer factory under the given name. Calls Fatal on
|
||||
// duplicate registration since dialer names must be unique at init time.
|
||||
func (r *dialerRegistry) Register(name string, v NewDialer) error {
|
||||
if err := r.registry.Register(name, v); err != nil {
|
||||
logger.Default().Fatal(err)
|
||||
|
||||
@@ -5,12 +5,18 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
// NewHandler is a factory function that creates a new handler.Handler
|
||||
// from the given options.
|
||||
type NewHandler func(opts ...handler.Option) handler.Handler
|
||||
|
||||
// handlerRegistry stores handler factory functions. Duplicate registrations
|
||||
// are treated as fatal errors (logger.Default().Fatal).
|
||||
type handlerRegistry struct {
|
||||
registry[NewHandler]
|
||||
}
|
||||
|
||||
// Register stores a handler factory under the given name. Calls Fatal on
|
||||
// duplicate registration since handler names must be unique at init time.
|
||||
func (r *handlerRegistry) Register(name string, v NewHandler) error {
|
||||
if err := r.registry.Register(name, v); err != nil {
|
||||
logger.Default().Fatal(err)
|
||||
|
||||
@@ -7,14 +7,18 @@ import (
|
||||
"github.com/go-gost/core/hop"
|
||||
)
|
||||
|
||||
// hopRegistry implements a hot-reload-safe registry for hop.Hop.
|
||||
type hopRegistry struct {
|
||||
registry[hop.Hop]
|
||||
}
|
||||
|
||||
// Register stores a Hop under the given name.
|
||||
func (r *hopRegistry) Register(name string, v hop.Hop) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered Hop.
|
||||
// Returns nil if name is empty.
|
||||
func (r *hopRegistry) Get(name string) hop.Hop {
|
||||
if name != "" {
|
||||
return &hopWrapper{name: name, r: r}
|
||||
|
||||
@@ -7,14 +7,18 @@ import (
|
||||
"github.com/go-gost/core/hosts"
|
||||
)
|
||||
|
||||
// hostsRegistry implements a hot-reload-safe registry for hosts.HostMapper.
|
||||
type hostsRegistry struct {
|
||||
registry[hosts.HostMapper]
|
||||
}
|
||||
|
||||
// Register stores a HostMapper under the given name.
|
||||
func (r *hostsRegistry) Register(name string, v hosts.HostMapper) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered HostMapper.
|
||||
// Returns nil if name is empty.
|
||||
func (r *hostsRegistry) Get(name string) hosts.HostMapper {
|
||||
if name != "" {
|
||||
return &hostsWrapper{name: name, r: r}
|
||||
|
||||
@@ -6,14 +6,18 @@ import (
|
||||
"github.com/go-gost/core/ingress"
|
||||
)
|
||||
|
||||
// ingressRegistry implements a hot-reload-safe registry for ingress.Ingress.
|
||||
type ingressRegistry struct {
|
||||
registry[ingress.Ingress]
|
||||
}
|
||||
|
||||
// Register stores an Ingress under the given name.
|
||||
func (r *ingressRegistry) Register(name string, v ingress.Ingress) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered Ingress.
|
||||
// Returns nil if name is empty.
|
||||
func (r *ingressRegistry) Get(name string) ingress.Ingress {
|
||||
if name != "" {
|
||||
return &ingressWrapper{name: name, r: r}
|
||||
|
||||
@@ -9,14 +9,18 @@ import (
|
||||
"github.com/go-gost/core/limiter/traffic"
|
||||
)
|
||||
|
||||
// trafficLimiterRegistry implements a hot-reload-safe registry for traffic.TrafficLimiter.
|
||||
type trafficLimiterRegistry struct {
|
||||
registry[traffic.TrafficLimiter]
|
||||
}
|
||||
|
||||
// Register stores a TrafficLimiter under the given name.
|
||||
func (r *trafficLimiterRegistry) Register(name string, v traffic.TrafficLimiter) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered TrafficLimiter.
|
||||
// Returns nil if name is empty.
|
||||
func (r *trafficLimiterRegistry) Get(name string) traffic.TrafficLimiter {
|
||||
if name != "" {
|
||||
return &trafficLimiterWrapper{name: name, r: r}
|
||||
@@ -49,14 +53,18 @@ func (w *trafficLimiterWrapper) Out(ctx context.Context, key string, opts ...lim
|
||||
return v.Out(ctx, key, opts...)
|
||||
}
|
||||
|
||||
// connLimiterRegistry implements a hot-reload-safe registry for conn.ConnLimiter.
|
||||
type connLimiterRegistry struct {
|
||||
registry[conn.ConnLimiter]
|
||||
}
|
||||
|
||||
// Register stores a ConnLimiter under the given name.
|
||||
func (r *connLimiterRegistry) Register(name string, v conn.ConnLimiter) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered ConnLimiter.
|
||||
// Returns nil if name is empty.
|
||||
func (r *connLimiterRegistry) Get(name string) conn.ConnLimiter {
|
||||
if name != "" {
|
||||
return &connLimiterWrapper{name: name, r: r}
|
||||
@@ -81,14 +89,18 @@ func (w *connLimiterWrapper) Limiter(key string) conn.Limiter {
|
||||
return v.Limiter(key)
|
||||
}
|
||||
|
||||
// rateLimiterRegistry implements a hot-reload-safe registry for rate.RateLimiter.
|
||||
type rateLimiterRegistry struct {
|
||||
registry[rate.RateLimiter]
|
||||
}
|
||||
|
||||
// Register stores a RateLimiter under the given name.
|
||||
func (r *rateLimiterRegistry) Register(name string, v rate.RateLimiter) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered RateLimiter.
|
||||
// Returns nil if name is empty.
|
||||
func (r *rateLimiterRegistry) Get(name string) rate.RateLimiter {
|
||||
if name != "" {
|
||||
return &rateLimiterWrapper{name: name, r: r}
|
||||
|
||||
@@ -5,12 +5,18 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
// NewListener is a factory function that creates a new listener.Listener
|
||||
// from the given options.
|
||||
type NewListener func(opts ...listener.Option) listener.Listener
|
||||
|
||||
// listenerRegistry stores listener factory functions. Duplicate registrations
|
||||
// are treated as fatal errors (logger.Default().Fatal).
|
||||
type listenerRegistry struct {
|
||||
registry[NewListener]
|
||||
}
|
||||
|
||||
// Register stores a listener factory under the given name. Calls Fatal on
|
||||
// duplicate registration since listener names must be unique at init time.
|
||||
func (r *listenerRegistry) Register(name string, v NewListener) error {
|
||||
if err := r.registry.Register(name, v); err != nil {
|
||||
logger.Default().Fatal(err)
|
||||
|
||||
@@ -4,10 +4,14 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
)
|
||||
|
||||
// loggerRegistry stores logger.Logger instances.
|
||||
// It uses the base registry methods without a wrapper since loggers
|
||||
// are looked up directly and do not need hot-reload proxying.
|
||||
type loggerRegistry struct {
|
||||
registry[logger.Logger]
|
||||
}
|
||||
|
||||
// Register stores a Logger under the given name.
|
||||
func (r *loggerRegistry) Register(name string, v logger.Logger) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
@@ -7,14 +7,18 @@ import (
|
||||
"github.com/go-gost/core/observer"
|
||||
)
|
||||
|
||||
// observerRegistry implements a hot-reload-safe registry for observer.Observer.
|
||||
type observerRegistry struct {
|
||||
registry[observer.Observer]
|
||||
}
|
||||
|
||||
// Register stores an Observer under the given name.
|
||||
func (r *observerRegistry) Register(name string, v observer.Observer) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered Observer.
|
||||
// Returns nil if name is empty.
|
||||
func (r *observerRegistry) Get(name string) observer.Observer {
|
||||
if name != "" {
|
||||
return &observerWrapper{name: name, r: r}
|
||||
|
||||
@@ -6,14 +6,18 @@ import (
|
||||
"github.com/go-gost/core/recorder"
|
||||
)
|
||||
|
||||
// recorderRegistry implements a hot-reload-safe registry for recorder.Recorder.
|
||||
type recorderRegistry struct {
|
||||
registry[recorder.Recorder]
|
||||
}
|
||||
|
||||
// Register stores a Recorder under the given name.
|
||||
func (r *recorderRegistry) Register(name string, v recorder.Recorder) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered Recorder.
|
||||
// Returns nil if name is empty.
|
||||
func (r *recorderRegistry) Get(name string) recorder.Recorder {
|
||||
if name != "" {
|
||||
return &recorderWrapper{name: name, r: r}
|
||||
|
||||
+37
-1
@@ -26,6 +26,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrDup is returned by Register when a value is already registered
|
||||
// under the given name.
|
||||
ErrDup = errors.New("registry: duplicate object")
|
||||
)
|
||||
|
||||
@@ -56,10 +58,14 @@ var (
|
||||
loggerReg reg.Registry[logger.Logger] = new(loggerRegistry)
|
||||
)
|
||||
|
||||
// registry is a sync.Map-backed generic named registry. It implements the
|
||||
// core Registry[T] interface and is embedded by every typed registry struct.
|
||||
type registry[T any] struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
// Register stores v under name. It returns ErrDup if name is already
|
||||
// registered. Empty names are silently ignored.
|
||||
func (r *registry[T]) Register(name string, v T) error {
|
||||
if name == "" {
|
||||
return nil
|
||||
@@ -71,20 +77,28 @@ func (r *registry[T]) Register(name string, v T) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unregister removes the value registered under name. If the value
|
||||
// implements io.Closer, Close is called before removal; close errors
|
||||
// are logged but do not prevent deletion.
|
||||
func (r *registry[T]) Unregister(name string) {
|
||||
if v, ok := r.m.Load(name); ok {
|
||||
if closer, ok := v.(io.Closer); ok {
|
||||
closer.Close()
|
||||
if err := closer.Close(); err != nil {
|
||||
logger.Default().Errorf("registry: close %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
r.m.Delete(name)
|
||||
}
|
||||
}
|
||||
|
||||
// IsRegistered reports whether a value is registered under name.
|
||||
func (r *registry[T]) IsRegistered(name string) bool {
|
||||
_, ok := r.m.Load(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Get returns the value registered under name, or the zero value of T
|
||||
// if name is empty or not registered.
|
||||
func (r *registry[T]) Get(name string) (t T) {
|
||||
if name == "" {
|
||||
return
|
||||
@@ -94,6 +108,7 @@ func (r *registry[T]) Get(name string) (t T) {
|
||||
return
|
||||
}
|
||||
|
||||
// GetAll returns a snapshot of all registered name-to-value mappings.
|
||||
func (r *registry[T]) GetAll() (m map[string]T) {
|
||||
m = make(map[string]T)
|
||||
r.m.Range(func(key, value any) bool {
|
||||
@@ -105,86 +120,107 @@ func (r *registry[T]) GetAll() (m map[string]T) {
|
||||
return
|
||||
}
|
||||
|
||||
// ListenerRegistry returns the global registry of listener factory functions.
|
||||
func ListenerRegistry() reg.Registry[NewListener] {
|
||||
return listenerReg
|
||||
}
|
||||
|
||||
// HandlerRegistry returns the global registry of handler factory functions.
|
||||
func HandlerRegistry() reg.Registry[NewHandler] {
|
||||
return handlerReg
|
||||
}
|
||||
|
||||
// DialerRegistry returns the global registry of dialer factory functions.
|
||||
func DialerRegistry() reg.Registry[NewDialer] {
|
||||
return dialerReg
|
||||
}
|
||||
|
||||
// ConnectorRegistry returns the global registry of connector factory functions.
|
||||
func ConnectorRegistry() reg.Registry[NewConnector] {
|
||||
return connectorReg
|
||||
}
|
||||
|
||||
// ServiceRegistry returns the global registry of service instances.
|
||||
func ServiceRegistry() reg.Registry[service.Service] {
|
||||
return serviceReg
|
||||
}
|
||||
|
||||
// ChainRegistry returns the global registry of chain instances.
|
||||
func ChainRegistry() reg.Registry[chain.Chainer] {
|
||||
return chainReg
|
||||
}
|
||||
|
||||
// HopRegistry returns the global registry of hop instances.
|
||||
func HopRegistry() reg.Registry[hop.Hop] {
|
||||
return hopReg
|
||||
}
|
||||
|
||||
// AutherRegistry returns the global registry of authenticator instances.
|
||||
func AutherRegistry() reg.Registry[auth.Authenticator] {
|
||||
return autherReg
|
||||
}
|
||||
|
||||
// AdmissionRegistry returns the global registry of admission instances.
|
||||
func AdmissionRegistry() reg.Registry[admission.Admission] {
|
||||
return admissionReg
|
||||
}
|
||||
|
||||
// BypassRegistry returns the global registry of bypass instances.
|
||||
func BypassRegistry() reg.Registry[bypass.Bypass] {
|
||||
return bypassReg
|
||||
}
|
||||
|
||||
// ResolverRegistry returns the global registry of resolver instances.
|
||||
func ResolverRegistry() reg.Registry[resolver.Resolver] {
|
||||
return resolverReg
|
||||
}
|
||||
|
||||
// HostsRegistry returns the global registry of host mapper instances.
|
||||
func HostsRegistry() reg.Registry[hosts.HostMapper] {
|
||||
return hostsReg
|
||||
}
|
||||
|
||||
// RecorderRegistry returns the global registry of recorder instances.
|
||||
func RecorderRegistry() reg.Registry[recorder.Recorder] {
|
||||
return recorderReg
|
||||
}
|
||||
|
||||
// TrafficLimiterRegistry returns the global registry of traffic limiter instances.
|
||||
func TrafficLimiterRegistry() reg.Registry[traffic.TrafficLimiter] {
|
||||
return trafficLimiterReg
|
||||
}
|
||||
|
||||
// ConnLimiterRegistry returns the global registry of connection limiter instances.
|
||||
func ConnLimiterRegistry() reg.Registry[conn.ConnLimiter] {
|
||||
return connLimiterReg
|
||||
}
|
||||
|
||||
// RateLimiterRegistry returns the global registry of rate limiter instances.
|
||||
func RateLimiterRegistry() reg.Registry[rate.RateLimiter] {
|
||||
return rateLimiterReg
|
||||
}
|
||||
|
||||
// IngressRegistry returns the global registry of ingress instances.
|
||||
func IngressRegistry() reg.Registry[ingress.Ingress] {
|
||||
return ingressReg
|
||||
}
|
||||
|
||||
// RouterRegistry returns the global registry of router instances.
|
||||
func RouterRegistry() reg.Registry[router.Router] {
|
||||
return routerReg
|
||||
}
|
||||
|
||||
// SDRegistry returns the global registry of service discovery instances.
|
||||
func SDRegistry() reg.Registry[sd.SD] {
|
||||
return sdReg
|
||||
}
|
||||
|
||||
// ObserverRegistry returns the global registry of observer instances.
|
||||
func ObserverRegistry() reg.Registry[observer.Observer] {
|
||||
return observerReg
|
||||
}
|
||||
|
||||
// LoggerRegistry returns the global registry of logger instances.
|
||||
func LoggerRegistry() reg.Registry[logger.Logger] {
|
||||
return loggerReg
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,14 +7,18 @@ import (
|
||||
"github.com/go-gost/core/resolver"
|
||||
)
|
||||
|
||||
// resolverRegistry implements a hot-reload-safe registry for resolver.Resolver.
|
||||
type resolverRegistry struct {
|
||||
registry[resolver.Resolver]
|
||||
}
|
||||
|
||||
// Register stores a Resolver under the given name.
|
||||
func (r *resolverRegistry) Register(name string, v resolver.Resolver) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered Resolver.
|
||||
// Returns nil if name is empty.
|
||||
func (r *resolverRegistry) Get(name string) resolver.Resolver {
|
||||
if name != "" {
|
||||
return &resolverWrapper{name: name, r: r}
|
||||
|
||||
@@ -6,14 +6,18 @@ import (
|
||||
"github.com/go-gost/core/router"
|
||||
)
|
||||
|
||||
// routerRegistry implements a hot-reload-safe registry for router.Router.
|
||||
type routerRegistry struct {
|
||||
registry[router.Router]
|
||||
}
|
||||
|
||||
// Register stores a Router under the given name.
|
||||
func (r *routerRegistry) Register(name string, v router.Router) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered Router.
|
||||
// Returns nil if name is empty.
|
||||
func (r *routerRegistry) Get(name string) router.Router {
|
||||
if name != "" {
|
||||
return &routerWrapper{name: name, r: r}
|
||||
|
||||
@@ -6,14 +6,18 @@ import (
|
||||
"github.com/go-gost/core/sd"
|
||||
)
|
||||
|
||||
// sdRegistry implements a hot-reload-safe registry for sd.SD.
|
||||
type sdRegistry struct {
|
||||
registry[sd.SD]
|
||||
}
|
||||
|
||||
// Register stores an SD under the given name.
|
||||
func (r *sdRegistry) Register(name string, v sd.SD) error {
|
||||
return r.registry.Register(name, v)
|
||||
}
|
||||
|
||||
// Get returns a wrapper that delegates to the currently registered SD.
|
||||
// Returns nil if name is empty.
|
||||
func (r *sdRegistry) Get(name string) sd.SD {
|
||||
if name != "" {
|
||||
return &sdWrapper{name: name, r: r}
|
||||
|
||||
@@ -4,6 +4,9 @@ import (
|
||||
"github.com/go-gost/core/service"
|
||||
)
|
||||
|
||||
// serviceRegistry stores service.Service instances.
|
||||
// It uses the base registry methods without a wrapper since services
|
||||
// are looked up directly by name during startup.
|
||||
type serviceRegistry struct {
|
||||
registry[service.Service]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user