| 1 | package steward |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "sync" |
| 6 | |
| 7 | "github.com/soverenio/vanilla/throw" |
| 8 | ) |
| 9 | |
| 10 | func mustValueFromContext[T any](ctx context.Context, key any) T { |
| 11 | val, ok := ctx.Value(key).(T) |
| 12 | if !ok { |
| 13 | panic("missing required context value") |
| 14 | } |
| 15 | return val |
| 16 | } |
| 17 | |
| 18 | type initScopeKey struct{} |
| 19 | |
| 20 | type InitScope interface { |
| 21 | AddComponent(ctx context.Context, components ...Asset) // we should put here already initialized components |
| 22 | } |
| 23 | |
| 24 | type initScope struct { |
| 25 | lock sync.Mutex |
| 26 | status bool |
| 27 | manager *Manager |
| 28 | } |
| 29 | |
| 30 | func (m *initScope) AddComponent(ctx context.Context, components ...Asset) { |
| 31 | m.lock.Lock() |
| 32 | defer m.lock.Unlock() |
| 33 | |
| 34 | if !m.status { |
| 35 | panic(throw.IllegalState()) // initScope.AddComponent was called after component.Init was stopped |
| 36 | } |
| 37 | |
| 38 | for _, component := range components { |
| 39 | svcComponent, ok := component.(*serviceAsset) |
| 40 | if !ok { |
| 41 | continue |
| 42 | } |
| 43 | |
| 44 | m.manager.inject(ctx, svcComponent) |
| 45 | } |
| 46 | |
| 47 | m.manager._addComponent(ctx, components...) |
| 48 | } |
| 49 | |
| 50 | func (m *initScope) Done(_ context.Context) { |
| 51 | m.lock.Lock() |
| 52 | defer m.lock.Unlock() |
| 53 | |
| 54 | if !m.status { |
| 55 | panic(throw.IllegalState()) // initScope.Done was called twice |
| 56 | } |
| 57 | m.status = false |
| 58 | } |
| 59 | |
| 60 | func InitScopeFromContext(ctx context.Context) InitScope { |
| 61 | return mustValueFromContext[InitScope](ctx, initScopeKey{}) |
| 62 | } |
| 63 | |
| 64 | func withInitScope(ctx context.Context, manager *Manager) (context.Context, *initScope) { |
| 65 | wrapper := &initScope{manager: manager, status: true} |
| 66 | return context.WithValue(ctx, initScopeKey{}, wrapper), wrapper |
| 67 | } |
| 68 | |
| 69 | type startScopeKey struct{} |
| 70 | |
| 71 | type StartScope interface { |
| 72 | CreateChildManager(ctx context.Context) *Manager |
| 73 | } |
| 74 | |
| 75 | type startScope struct { |
| 76 | lock sync.Mutex |
| 77 | status bool |
| 78 | manager *Manager |
| 79 | } |
| 80 | |
| 81 | func StartScopeFromContext(ctx context.Context) StartScope { |
| 82 | return mustValueFromContext[StartScope](ctx, startScopeKey{}) |
| 83 | } |
| 84 | |
| 85 | func WithStartScope(ctx context.Context, manager *Manager) (context.Context, StartScope) { |
| 86 | return withStartScope(ctx, manager) |
| 87 | } |
| 88 | |
| 89 | func withStartScope(ctx context.Context, manager *Manager) (context.Context, *startScope) { |
| 90 | wrapper := &startScope{manager: manager, status: true} |
| 91 | return context.WithValue(ctx, startScopeKey{}, wrapper), wrapper |
| 92 | } |
| 93 | |
| 94 | func (m *startScope) CreateChildManager(_ context.Context) *Manager { |
| 95 | m.lock.Lock() |
| 96 | defer m.lock.Unlock() |
| 97 | |
| 98 | if !m.status { |
| 99 | panic(throw.IllegalState()) // startScope.CreateChildManager was called after component.Start was stopped |
| 100 | } |
| 101 | |
| 102 | return NewChildManager(m.manager) |
| 103 | } |
| 104 | |
| 105 | func (m *startScope) Done(_ context.Context) { |
| 106 | m.lock.Lock() |
| 107 | defer m.lock.Unlock() |
| 108 | |
| 109 | if !m.status { |
| 110 | panic(throw.IllegalState()) // startScope.Done was called twice |
| 111 | } |
| 112 | m.status = false |
| 113 | } |
| 114 | |