-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathruntime.go
More file actions
149 lines (130 loc) · 3.82 KB
/
runtime.go
File metadata and controls
149 lines (130 loc) · 3.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// SPDX-License-Identifier: EUPL-1.2
// Runtime helpers for the Core framework.
// ServiceRuntime is embedded by consumer services.
// Runtime is the GUI binding container (e.g., Wails).
package core
import (
"context"
"maps"
"slices"
)
// --- ServiceRuntime (embedded by consumer services) ---
// ServiceRuntime is embedded in services to provide access to the Core and typed options.
type ServiceRuntime[T any] struct {
core *Core
opts T
}
// NewServiceRuntime creates a ServiceRuntime for a service constructor.
func NewServiceRuntime[T any](c *Core, opts T) *ServiceRuntime[T] {
return &ServiceRuntime[T]{core: c, opts: opts}
}
func (r *ServiceRuntime[T]) Core() *Core { return r.core }
func (r *ServiceRuntime[T]) Options() T { return r.opts }
func (r *ServiceRuntime[T]) Config() *Config { return r.core.Config() }
// --- Lifecycle ---
// ServiceStartup runs OnStart for all registered services that have one.
func (c *Core) ServiceStartup(ctx context.Context, options any) Result {
c.shutdown.Store(false)
c.context, c.cancel = context.WithCancel(ctx)
startables := c.Startables()
if startables.OK {
for _, s := range startables.Value.([]*Service) {
if err := ctx.Err(); err != nil {
return Result{err, false}
}
r := s.OnStart()
if !r.OK {
return r
}
}
}
c.ACTION(ActionServiceStartup{})
return Result{OK: true}
}
// ServiceShutdown drains background tasks, then stops all registered services.
func (c *Core) ServiceShutdown(ctx context.Context) Result {
c.shutdown.Store(true)
c.cancel() // signal all context-aware tasks to stop
c.ACTION(ActionServiceShutdown{})
// Drain background tasks before stopping services.
done := make(chan struct{})
go func() {
c.waitGroup.Wait()
close(done)
}()
select {
case <-done:
case <-ctx.Done():
return Result{ctx.Err(), false}
}
// Stop services
var firstErr error
stoppables := c.Stoppables()
if stoppables.OK {
for _, s := range stoppables.Value.([]*Service) {
if err := ctx.Err(); err != nil {
return Result{err, false}
}
r := s.OnStop()
if !r.OK && firstErr == nil {
if e, ok := r.Value.(error); ok {
firstErr = e
} else {
firstErr = E("core.ServiceShutdown", Sprint("service OnStop failed: ", r.Value), nil)
}
}
}
}
if firstErr != nil {
return Result{firstErr, false}
}
return Result{OK: true}
}
// --- Runtime DTO (GUI binding) ---
// Runtime is the container for GUI runtimes (e.g., Wails).
type Runtime struct {
app any
Core *Core
}
// ServiceFactory defines a function that creates a Service.
type ServiceFactory func() Result
// NewWithFactories creates a Runtime with the provided service factories.
func NewWithFactories(app any, factories map[string]ServiceFactory) Result {
c := New(Options{{Key: "name", Value: "core"}})
c.app.Runtime = app
names := slices.Sorted(maps.Keys(factories))
for _, name := range names {
factory := factories[name]
if factory == nil {
continue
}
r := factory()
if !r.OK {
cause, _ := r.Value.(error)
return Result{E("core.NewWithFactories", Concat("factory \"", name, "\" failed"), cause), false}
}
svc, ok := r.Value.(Service)
if !ok {
return Result{E("core.NewWithFactories", Concat("factory \"", name, "\" returned non-Service type"), nil), false}
}
sr := c.Service(name, svc)
if !sr.OK {
return sr
}
}
return Result{&Runtime{app: app, Core: c}, true}
}
// NewRuntime creates a Runtime with no custom services.
func NewRuntime(app any) Result {
return NewWithFactories(app, map[string]ServiceFactory{})
}
func (r *Runtime) ServiceName() string { return "Core" }
func (r *Runtime) ServiceStartup(ctx context.Context, options any) Result {
return r.Core.ServiceStartup(ctx, options)
}
func (r *Runtime) ServiceShutdown(ctx context.Context) Result {
if r.Core != nil {
return r.Core.ServiceShutdown(ctx)
}
return Result{OK: true}
}