Documentation
¶
Overview ¶
Package dispatcher provides a generic, type-safe function dispatcher with optional middleware. It enables runtime dispatching of values to registered handlers based on their concrete type.
Example ¶
package main import ( "context" "fmt" "github.com/struct0x/dispatcher" ) func loggingMiddleware[T any]() dispatcher.Middleware[T] { return func(next dispatcher.HandlerFunc[T]) dispatcher.HandlerFunc[T] { return func(ctx context.Context, event T) error { fmt.Printf("Processing event: %T\n", event) err := next(ctx, event) if err != nil { fmt.Printf("Error processing event: %v\n", err) return err } fmt.Printf("Successfully processed event: %T\n", event) return nil } } } func main() { reg := dispatcher.NewRegistry() // Define event types type UserCreated struct { ID int Name string } type OrderPlaced struct { OrderID string Amount float64 } type Unknown struct { Foo string } // Create middleware using the helper function validationMiddleware := dispatcher.MiddlewareFunc(func(ctx context.Context, event UserCreated) (bool, error) { if event.ID <= 0 { return false, fmt.Errorf("invalid user ID: %d", event.ID) } if event.Name == "" { return false, fmt.Errorf("user name cannot be empty") } return true, nil // Continue processing }) // Register handlers for different types dispatcher.Register( reg, func(ctx context.Context, event UserCreated) error { fmt.Printf("User created: %s (ID: %d)\n", event.Name, event.ID) return nil }, validationMiddleware, loggingMiddleware[UserCreated](), ) dispatcher.Register( reg, func(ctx context.Context, event OrderPlaced) error { fmt.Printf("Order placed: %s for $%.2f\n", event.OrderID, event.Amount) return nil }, loggingMiddleware[OrderPlaced](), ) // Dispatch events ctx := context.Background() _ = dispatcher.Dispatch(reg, ctx, UserCreated{ID: 1, Name: "Alice"}) _ = dispatcher.Dispatch(reg, ctx, OrderPlaced{OrderID: "ORD-001", Amount: 99.99}) sealedReg := reg.Seal() _ = dispatcher.Dispatch(sealedReg, ctx, UserCreated{ID: 2, Name: "Alice"}) _ = dispatcher.Dispatch(sealedReg, ctx, OrderPlaced{OrderID: "ORD-002", Amount: 99.99}) if err := dispatcher.Dispatch(reg, ctx, Unknown{Foo: "bar"}); err != nil { fmt.Printf("Dispatch err: %v", err) } }
Output: Processing event: dispatcher_test.UserCreated User created: Alice (ID: 1) Successfully processed event: dispatcher_test.UserCreated Processing event: dispatcher_test.OrderPlaced Order placed: ORD-001 for $99.99 Successfully processed event: dispatcher_test.OrderPlaced Processing event: dispatcher_test.UserCreated User created: Alice (ID: 2) Successfully processed event: dispatcher_test.UserCreated Processing event: dispatcher_test.OrderPlaced Order placed: ORD-002 for $99.99 Successfully processed event: dispatcher_test.OrderPlaced Dispatch err: dispatcher: handler not found for type dispatcher_test.Unknown
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrHandlerNotFound = errors.New("handler not found")
ErrHandlerNotFound is returned when no handler is found for the given value's type.
Functions ¶
func Dispatch ¶
Dispatch dispatches the given value to a registered handler based on its concrete type. It returns ErrHandlerNotFound if no handler is registered for the value's type.
func Register ¶
func Register[T any](reg registry, handler HandlerFunc[T], middleware ...Middleware[T])
Register adds a handler for values of type T, with optional middleware.
If a handler for the same type T has already been registered, it will be replaced by the new handler and middleware chain.
Middleware is applied outermost first (i.e., the last middleware wraps the others).
Types ¶
type HandlerFunc ¶
HandlerFunc is a type-safe handler for values of type T. It receives a context and a value, and may return an error.
type Middleware ¶
type Middleware[T any] func(next HandlerFunc[T]) HandlerFunc[T]
Middleware is a type-safe wrapper around a HandlerFunc. It allows injecting logic before/after the handler.
func MiddlewareFunc ¶
MiddlewareFunc is a simple convenience function to create middleware from a function that optionally short-circuits the call chain.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds registered type-safe handlers. It is used during application setup to register handlers for specific types.
Use NewRegistry() to create one, then Register() handlers, and finally call Seal() to create an immutable SealedRegistry.
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry creates a new empty Registry for registering handlers.
func (*Registry) Seal ¶
func (r *Registry) Seal() *SealedRegistry
Seal finalizes the Registry and returns a SealedRegistry.
The resulting SealedRegistry is immutable and safe for concurrent dispatch with no mutex overhead.
type SealedRegistry ¶
type SealedRegistry struct {
// contains filtered or unexported fields
}
SealedRegistry is an immutable, thread-safe dispatcher used at runtime.
It is created from a Registry using Seal(), and allows concurrent Dispatch() without mutex overhead, as its internal map is read-only after sealing.