summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-18 09:24:22 +0200
committerPaul Buetow <paul@buetow.org>2026-03-18 09:24:22 +0200
commitdbb98cd8dbd70dd92b8a541653e64c45b18348fa (patch)
treef2faa385564a4700c608bcf8f2ffc638c1c0bd27
parent630ea0ff27b8e9ff9287eaaf67660845406a19a6 (diff)
refactor: split TraceRuntimeBindings into RuntimePublisher and RuntimeState (task 427/ISP)
TraceRuntimeBindings mixed 4 setter methods (injecting data into TUI) with 4 getter methods (reading persistent TUI-owned state), violating ISP. Split into two focused interfaces: - RuntimePublisher: SetDashboardSnapshotSource, SetEventStreamSource, SetLiveTrie, SetProbeManager — the write/inject side - RuntimeState: StreamBuffer, Recorder, StreamSequencer, FilterEpoch — the read/persistent-state side TraceRuntimeBindings now embeds both, preserving all existing call sites. RuntimePublisherFromContext() added so callers that only inject data do not see the getter surface. Three such callers are narrowed: setupBPFModule, tuiTestFlamesStarter, tuiTestLiveFlamesStarter. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
-rw-r--r--internal/ior.go6
-rw-r--r--internal/ior_bpfsetup.go4
-rw-r--r--internal/tui/tui.go34
3 files changed, 36 insertions, 8 deletions
diff --git a/internal/ior.go b/internal/ior.go
index 4bd5c8a..ab52299 100644
--- a/internal/ior.go
+++ b/internal/ior.go
@@ -108,7 +108,8 @@ func validateRunConfig(cfg flags.Config) error {
func tuiTestFlamesStarter(cfg flags.Config) tui.TraceStarter {
return func(ctx context.Context) error {
engine, streamBuf, liveTrie := buildTestFlamesRuntime(cfg)
- if bindings, ok := tui.RuntimeBindingsFromContext(ctx); ok {
+ // Only setter methods are needed here; use the narrower publisher interface.
+ if bindings, ok := tui.RuntimePublisherFromContext(ctx); ok {
bindings.SetDashboardSnapshotSource(engine)
bindings.SetEventStreamSource(streamBuf)
bindings.SetLiveTrie(liveTrie)
@@ -120,7 +121,8 @@ func tuiTestFlamesStarter(cfg flags.Config) tui.TraceStarter {
func tuiTestLiveFlamesStarter(cfg flags.Config) tui.TraceStarter {
return func(ctx context.Context) error {
engine, streamBuf, liveTrie := buildTestLiveFlamesRuntime(ctx, cfg)
- if bindings, ok := tui.RuntimeBindingsFromContext(ctx); ok {
+ // Only setter methods are needed here; use the narrower publisher interface.
+ if bindings, ok := tui.RuntimePublisherFromContext(ctx); ok {
bindings.SetDashboardSnapshotSource(engine)
bindings.SetEventStreamSource(streamBuf)
bindings.SetLiveTrie(liveTrie)
diff --git a/internal/ior_bpfsetup.go b/internal/ior_bpfsetup.go
index cf0b112..3500106 100644
--- a/internal/ior_bpfsetup.go
+++ b/internal/ior_bpfsetup.go
@@ -70,7 +70,9 @@ func setupBPFModule(parentCtx context.Context, cfg flags.Config) (*bpf.Module, *
bpfModule.Close()
return nil, nil, releaseBindings, setupBPFModuleError("attach probes", err)
}
- if bindings, ok := tui.RuntimeBindingsFromContext(parentCtx); ok {
+ // setupBPFModule only injects the probe manager; it does not read TUI state,
+ // so RuntimePublisher is the correct narrower interface to use here.
+ if bindings, ok := tui.RuntimePublisherFromContext(parentCtx); ok {
bindings.SetProbeManager(mgr)
releaseBindings = func() { bindings.SetProbeManager(nil) }
}
diff --git a/internal/tui/tui.go b/internal/tui/tui.go
index 432fc14..252e722 100644
--- a/internal/tui/tui.go
+++ b/internal/tui/tui.go
@@ -58,19 +58,31 @@ type ProbeManager interface {
ActiveCount() (int, int)
}
-// TraceRuntimeBindings allows a trace starter to publish runtime dependencies
-// (snapshot source, stream source, probe manager) into the active TUI model.
-type TraceRuntimeBindings interface {
+// RuntimePublisher is the write side of the TUI runtime contract.
+// A trace starter calls these methods to inject live data into the active TUI.
+type RuntimePublisher interface {
SetDashboardSnapshotSource(source SnapshotSource)
SetEventStreamSource(source eventstream.Source)
SetLiveTrie(liveTrie flamegraphtui.LiveTrieSource)
SetProbeManager(manager ProbeManager)
+}
+
+// RuntimeState is the read side of the TUI runtime contract.
+// A trace starter calls these methods to obtain persistent state owned by the TUI.
+type RuntimeState interface {
StreamBuffer() eventstream.Source
Recorder() *parquet.Recorder
StreamSequencer() *eventstream.Sequencer
FilterEpoch() uint64
}
+// TraceRuntimeBindings composes RuntimePublisher and RuntimeState so a trace
+// starter can both inject live data and read persistent TUI-owned state.
+type TraceRuntimeBindings interface {
+ RuntimePublisher
+ RuntimeState
+}
+
type runtimeBindingsContextKey struct{}
type traceFiltersContextKey struct{}
@@ -200,8 +212,9 @@ func (r *runtimeBindings) resetDashboardSnapshotSource() *statsengine.Snapshot {
return nil
}
-// RuntimeBindingsFromContext returns model-scoped trace bindings when the
-// context was created by the TUI.
+// RuntimeBindingsFromContext returns the full TraceRuntimeBindings when the
+// context was created by the TUI. Use RuntimePublisherFromContext when only
+// write access is needed.
func RuntimeBindingsFromContext(ctx context.Context) (TraceRuntimeBindings, bool) {
bindings, ok := ctx.Value(runtimeBindingsContextKey{}).(TraceRuntimeBindings)
if !ok || bindings == nil {
@@ -210,6 +223,17 @@ func RuntimeBindingsFromContext(ctx context.Context) (TraceRuntimeBindings, bool
return bindings, true
}
+// RuntimePublisherFromContext returns only the RuntimePublisher side of the
+// TUI bindings. Use this when the caller only injects data and does not need
+// to read persistent TUI state.
+func RuntimePublisherFromContext(ctx context.Context) (RuntimePublisher, bool) {
+ bindings, ok := ctx.Value(runtimeBindingsContextKey{}).(RuntimePublisher)
+ if !ok || bindings == nil {
+ return nil, false
+ }
+ return bindings, true
+}
+
// ContextWithRuntimeBindings stores trace runtime bindings on the context.
func ContextWithRuntimeBindings(ctx context.Context, bindings TraceRuntimeBindings) context.Context {
return context.WithValue(ctx, runtimeBindingsContextKey{}, bindings)