hub

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 11, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package hub coordinates a family of visualizer windows: one leader process spawns child windows of its own executable and relays JSON messages between them over the children's stdin and stdout, so every window shows the same state.

The message type belongs to each application; a Hub is generic over it. A Config supplies a routing policy that sorts each inbound message into a Route: RouteState caches and rebroadcasts shared state, RouteBroadcast fans a message out once, and RouteSpawn and RouteCloseNewest open and close windows. Most applications never touch a Hub directly: RunLeader wires up the leader window and RunChild a spawned one, each handing the window a Link to speak through. Delivery is lossy by design through TrySend, so a stalled window never stalls the hub.

Index

Examples

Constants

View Source
const DefaultMaxWindows = 16

DefaultMaxWindows is the window cap applied when Config.MaxWindows is zero.

Variables

This section is empty.

Functions

func RunChild

func RunChild[M any](run func(Link[M]) error) error

RunChild runs a child window wired to the leader over stdin/stdout: run receives the child's Link and blocks until the window closes. The Link's In channel closes when the leader disappears.

Example

ExampleRunChild is the counterpart run by spawned windows: the Link speaks to the leader over stdin/stdout, and In closes when the leader goes away.

package main

import (
	"fmt"

	"github.com/danielriddell21/crucible/hub"
)

// Msg is the application's own wire type; the hub is generic over it.
type Msg struct {
	Type string  `json:"t"`
	Yaw  float32 `json:"yaw,omitempty"`
}

func main() {
	err := hub.RunChild(func(l hub.Link[Msg]) error {
		for m := range l.In {
			_ = m // apply the shared state to this window
		}
		return nil
	})
	if err != nil {
		fmt.Println(err)
	}
}

func RunLeader

func RunLeader[M any](cfg Config[M], run func(Link[M]) error) error

RunLeader starts a hub for the leader window: run receives the leader's Link and blocks until the window closes (conventionally by calling the application's gui.Run). Child windows spawned along the way are killed on return.

Example

ExampleRunLeader shows the leader side of a multi-window visualizer: the Route policy maps the app's message types onto hub behaviours, and the window loop talks through the Link. Child processes run the same binary with ExampleRunChild's wiring.

package main

import (
	"fmt"
	"os"

	"github.com/danielriddell21/crucible/hub"
)

// Msg is the application's own wire type; the hub is generic over it.
type Msg struct {
	Type string  `json:"t"`
	Yaw  float32 `json:"yaw,omitempty"`
}

func main() {
	cfg := hub.Config[Msg]{
		Self: os.Args[0],
		ChildArgs: func(idx int) []string {
			return []string{"view", fmt.Sprintf("--child=%d", idx)}
		},
		Route: func(m Msg) hub.Route {
			switch m.Type {
			case "state":
				return hub.RouteState
			case "add":
				return hub.RouteSpawn
			case "remove":
				return hub.RouteCloseNewest
			}
			return hub.RouteNone
		},
		Quit: Msg{Type: "quit"},
	}

	err := hub.RunLeader(cfg, func(l hub.Link[Msg]) error {
		// gui.Run(gui.Config{Link: l, ...}) — the window sends its state on
		// l.Out and applies messages arriving on l.In.
		return nil
	})
	if err != nil {
		fmt.Println(err)
	}
}

func TrySend

func TrySend[M any](ch chan<- M, m M)

TrySend delivers m without blocking, dropping it when the channel is full. Window links are lossy by design: a stalled window must never stall the hub.

Types

type Config

type Config[M any] struct {
	// Self is the executable to spawn for child windows, conventionally
	// os.Args[0].
	Self string
	// ChildArgs returns the arguments for the idx-th child window, e.g.
	// {"view", "--child=1"}.
	ChildArgs func(idx int) []string
	// Route classifies inbound messages.
	Route func(M) Route
	// Quit is the message sent to a child window to make it close.
	Quit M
	// MaxWindows caps the number of simultaneous windows. Zero means the
	// conventional 16.
	MaxWindows int
}

Config describes how a Hub runs. M is the application's message type, which must marshal to JSON.

type Hub

type Hub[M any] struct {
	// contains filtered or unexported fields
}

Hub relays messages between the leader window and its children. Create one with New, register the leader with Hub.AddParticipant, then start Hub.Run in a goroutine.

func New

func New[M any](cfg Config[M]) *Hub[M]

New returns a hub with no participants.

func (*Hub[M]) AddParticipant

func (h *Hub[M]) AddParticipant(out chan M, cmd *exec.Cmd) int

AddParticipant registers a window that receives messages on out and returns its id. cmd is the child process behind the window, or nil for the in-process leader. A late joiner immediately receives the cached state, if any.

func (*Hub[M]) CloseNewest

func (h *Hub[M]) CloseNewest()

CloseNewest sends the quit message to the most recently spawned child window, if any.

func (*Hub[M]) Inject

func (h *Hub[M]) Inject(src int, m M)

Inject feeds a message from participant src into the hub, as if it had arrived from that window's process.

func (*Hub[M]) Run

func (h *Hub[M]) Run()

Run dispatches messages until Shutdown. Run it in its own goroutine.

func (*Hub[M]) Shutdown

func (h *Hub[M]) Shutdown()

Shutdown kills every child window process and stops Run.

func (*Hub[M]) SpawnChild

func (h *Hub[M]) SpawnChild()

SpawnChild starts another child window process and wires it into the hub, unless the window cap is reached.

type Link[M any] struct {
	In  <-chan M
	Out chan<- M
}

Link is the pair of channels a window uses to talk to the hub: In carries messages from the other windows, Out carries this window's messages to them. In is closed when the leader goes away, so a child window can treat that as its signal to terminate.

type Route

type Route int

Route says what the hub should do with an inbound message.

const (
	// RouteNone ignores the message.
	RouteNone Route = iota
	// RouteState caches the message as the latest shared state, replays it
	// to windows that join later, and broadcasts it to every other window.
	RouteState
	// RouteBroadcast forwards the message to every other window once.
	RouteBroadcast
	// RouteSpawn opens another child window.
	RouteSpawn
	// RouteCloseNewest asks the most recently spawned child window to quit.
	RouteCloseNewest
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL