fix: move HandleIPCEvents discovery to New() post-construction
WithService is now a simple factory call — no reflect, no auto-registration. New() calls discoverHandlers() after all opts run, scanning Config for service instances that implement HandleIPCEvents. This eliminates both double-registration and empty-placeholder issues: - Factories wire their own lifecycle via c.Service() - HandleIPCEvents discovered once, after all services are registered - No tension between factory-registered and auto-discovered paths Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
3a9ac82275
commit
98d078130e
2 changed files with 30 additions and 37 deletions
42
contract.go
42
contract.go
|
|
@ -6,7 +6,6 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"reflect"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Message is the type for IPC broadcasts (fire-and-forget).
|
// Message is the type for IPC broadcasts (fire-and-forget).
|
||||||
|
|
@ -113,6 +112,10 @@ func New(opts ...CoreOption) Result {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Post-construction: discover IPC handlers on all registered services.
|
||||||
|
// Services that implement HandleIPCEvents get auto-wired to the bus.
|
||||||
|
c.discoverHandlers()
|
||||||
|
|
||||||
return Result{c, true}
|
return Result{c, true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,42 +144,7 @@ func WithOptions(opts Options) CoreOption {
|
||||||
// core.WithService(display.Register(nil))
|
// core.WithService(display.Register(nil))
|
||||||
func WithService(factory func(*Core) Result) CoreOption {
|
func WithService(factory func(*Core) Result) CoreOption {
|
||||||
return func(c *Core) Result {
|
return func(c *Core) Result {
|
||||||
r := factory(c)
|
return factory(c)
|
||||||
if !r.OK {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the factory returned a service instance, auto-discover and register.
|
|
||||||
// Only applies when the factory didn't register the service itself.
|
|
||||||
if r.Value != nil {
|
|
||||||
instance := r.Value
|
|
||||||
typeOf := reflect.TypeOf(instance)
|
|
||||||
if typeOf.Kind() == reflect.Ptr {
|
|
||||||
typeOf = typeOf.Elem()
|
|
||||||
}
|
|
||||||
pkgPath := typeOf.PkgPath()
|
|
||||||
parts := Split(pkgPath, "/")
|
|
||||||
name := Lower(parts[len(parts)-1])
|
|
||||||
|
|
||||||
if name != "" {
|
|
||||||
// Only auto-register if the factory didn't already do it
|
|
||||||
if sr := c.Service(name); !sr.OK {
|
|
||||||
c.Service(name, Service{})
|
|
||||||
|
|
||||||
// IPC handler discovery — only on auto-registered services
|
|
||||||
// to avoid double-registration when the factory already wired handlers
|
|
||||||
instanceValue := reflect.ValueOf(instance)
|
|
||||||
handlerMethod := instanceValue.MethodByName("HandleIPCEvents")
|
|
||||||
if handlerMethod.IsValid() {
|
|
||||||
if handler, ok := handlerMethod.Interface().(func(*Core, Message) Result); ok {
|
|
||||||
c.RegisterAction(handler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result{OK: true}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
25
core.go
25
core.go
|
|
@ -7,6 +7,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
@ -80,4 +81,28 @@ func (c *Core) Must(err error, op, msg string) {
|
||||||
c.log.Must(err, op, msg)
|
c.log.Must(err, op, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Post-Construction ---
|
||||||
|
|
||||||
|
// discoverHandlers scans Config for service instances that implement HandleIPCEvents.
|
||||||
|
// Called once after all WithService options have run — services are fully registered.
|
||||||
|
func (c *Core) discoverHandlers() {
|
||||||
|
if c.config == nil || c.config.ConfigOptions == nil || c.config.Settings == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.config.mu.RLock()
|
||||||
|
defer c.config.mu.RUnlock()
|
||||||
|
for _, val := range c.config.Settings {
|
||||||
|
if val == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
instanceValue := reflect.ValueOf(val)
|
||||||
|
handlerMethod := instanceValue.MethodByName("HandleIPCEvents")
|
||||||
|
if handlerMethod.IsValid() {
|
||||||
|
if handler, ok := handlerMethod.Interface().(func(*Core, Message) Result); ok {
|
||||||
|
c.RegisterAction(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Global Instance ---
|
// --- Global Instance ---
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue