5 changed files with 65 additions and 2 deletions
10
contract.go
10
contract.go
|
|
@ -112,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}
|
||||
}
|
||||
|
||||
|
|
@ -120,8 +124,10 @@ func New(opts ...CoreOption) Result {
|
|||
// core.WithOptions(core.Options{{Key: "name", Value: "myapp"}})
|
||||
func WithOptions(opts Options) CoreOption {
|
||||
return func(c *Core) Result {
|
||||
c.options = &opts
|
||||
if name := opts.String("name"); name != "" {
|
||||
cp := make(Options, len(opts))
|
||||
copy(cp, opts)
|
||||
c.options = &cp
|
||||
if name := cp.String("name"); name != "" {
|
||||
c.app.Name = name
|
||||
}
|
||||
return Result{OK: true}
|
||||
|
|
|
|||
25
core.go
25
core.go
|
|
@ -7,6 +7,7 @@ package core
|
|||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
|
@ -80,4 +81,28 @@ func (c *Core) Must(err error, op, msg string) {
|
|||
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 ---
|
||||
|
|
|
|||
15
core_test.go
15
core_test.go
|
|
@ -64,6 +64,21 @@ func TestNew_WithServiceLock_Good(t *testing.T) {
|
|||
assert.False(t, reg.OK)
|
||||
}
|
||||
|
||||
func TestNew_WithService_Bad_FailingOption(t *testing.T) {
|
||||
secondCalled := false
|
||||
r := New(
|
||||
WithService(func(c *Core) Result {
|
||||
return Result{Value: E("test", "intentional failure", nil), OK: false}
|
||||
}),
|
||||
WithService(func(c *Core) Result {
|
||||
secondCalled = true
|
||||
return Result{OK: true}
|
||||
}),
|
||||
)
|
||||
assert.False(t, r.OK)
|
||||
assert.False(t, secondCalled, "second option should not run after first fails")
|
||||
}
|
||||
|
||||
// --- Accessors ---
|
||||
|
||||
func TestAccessors_Good(t *testing.T) {
|
||||
|
|
|
|||
10
data_test.go
10
data_test.go
|
|
@ -9,6 +9,8 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// testFS exposes a filesystem rooted at ".", with testdata/ as a real directory entry.
|
||||
//
|
||||
//go:embed testdata
|
||||
var testFS embed.FS
|
||||
|
||||
|
|
@ -46,6 +48,14 @@ func TestData_ReadString_Good(t *testing.T) {
|
|||
assert.Equal(t, "hello from testdata\n", r.Value.(string))
|
||||
}
|
||||
|
||||
func TestData_ReadString_RootMount_Good(t *testing.T) {
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(Options{{Key: "name", Value: "app"}, {Key: "source", Value: testFS}, {Key: "path", Value: "."}})
|
||||
r := c.Data().ReadString("app/testdata/test.txt")
|
||||
assert.True(t, r.OK)
|
||||
assert.Equal(t, "hello from testdata\n", r.Value.(string))
|
||||
}
|
||||
|
||||
func TestData_ReadString_Bad(t *testing.T) {
|
||||
c := New().Value.(*Core)
|
||||
r := c.Data().ReadString("nonexistent/file.txt")
|
||||
|
|
|
|||
|
|
@ -39,6 +39,13 @@ func TestEmbed_ReadString_Good(t *testing.T) {
|
|||
assert.Equal(t, "hello from testdata\n", r.Value.(string))
|
||||
}
|
||||
|
||||
func TestEmbed_ReadString_RootMount_Good(t *testing.T) {
|
||||
emb := Mount(testFS, ".").Value.(*Embed)
|
||||
r := emb.ReadString("testdata/test.txt")
|
||||
assert.True(t, r.OK)
|
||||
assert.Equal(t, "hello from testdata\n", r.Value.(string))
|
||||
}
|
||||
|
||||
func TestEmbed_Open_Good(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
r := emb.Open("test.txt")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue