From 0e6fb74914fc3c071dbb785ccc02561d67f826bd Mon Sep 17 00:00:00 2001 From: Jay Date: Sat, 9 May 2026 10:42:09 -0400 Subject: [PATCH] wrote readme, updated api --- README.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 16 +++++------ 2 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..da162fd --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# go-mana-component + +Component identity propagation for layered Go libraries. + +Source: https://git.wisehodl.dev/jay/go-mana-component +Mirror: https://github.com/wisehodl/go-mana-component + +## What this library does + +- Injects a named component identity into a `context.Context` at library boundaries +- Propagates module identity and component hierarchy across layers +- Provides `slog` attributes for structured logging and a string map for generic consumers + +## What this library does not do + +`go-mana-component` does not log, does not define what a module or component +means semantically, and does not manage component lifecycles or dependencies. +It only carries identity. Interpretation, enforcement of naming conventions, +metrics, and tracing instrumentation all belong elsewhere. + +## Installation + +```bash +go get git.wisehodl.dev/jay/go-mana-component +``` + +If the primary repository is unavailable, use the `replace` directive in your `go.mod`: + +``` +replace git.wisehodl.dev/jay/go-mana-component => github.com/wisehodl/go-mana-component latest +``` + +## Usage + +As an example, the `go-honeybee` library uses a three-layer component hierarchy +for pools, workers, and connections. To provide structured logging, each +component accepts a component-aware context and an `slog.Handler` and then +constructs a logger internally. + +### At a library boundary + +A top-level constructor receives a context and creates a new component +identity. Injecting the component attributes on the logger allows it to carry +`module` and `path` automatically. + +```go +func NewPool(ctx context.Context, id string, handler slog.Handler) (*Pool, error) { + ctx = component.MustNew(ctx, "honeybee", "outbound_pool") + + attrs, _ := component.Attrs(ctx) + logger := slog.New(handler).WithAttrs(attrs).With(slog.String("pool_id", id)) + + return &Pool{ctx: ctx, logger: logger}, nil +} +``` + +### Descending into a sub-component + +A child constructor calls `MustExtend`, inheriting the module and extending the +path. No parent identifiers need to be passed as arguments. + +```go +func NewWorker(ctx context.Context, id string, handler slog.Handler) (*Worker, error) { + ctx = component.MustExtend(ctx, "outbound_worker") + + attrs, _ := component.Attrs(ctx) + logger := slog.New(handler).WithAttrs(attrs).With(slog.Any("peer_id", id)) + + return &Worker{ctx: ctx, logger: logger}, nil +} +``` + +At the connection layer, another `MustExtend` call extends the path to +`outbound_pool.outbound_worker.connection` with no additional plumbing. + +`GetFields` provides the component fields as a `map[string]string` for non-slog +consumers. + +## Testing + +```bash +go test ./... +``` diff --git a/main.go b/main.go index e6b127f..66c4cc2 100644 --- a/main.go +++ b/main.go @@ -41,8 +41,8 @@ func Get(ctx context.Context) (Component, bool) { return t, ok } -// MustStart is Start but panics on error. -func MustStart(ctx context.Context, module string, name string) context.Context { +// MustNew is New but panics on error. +func MustNew(ctx context.Context, module string, name string) context.Context { if ctx == nil { panic("context is nil") } @@ -55,8 +55,8 @@ func MustStart(ctx context.Context, module string, name string) context.Context return insert(ctx, module, name, []string{}) } -// MustNext is Next but panics on error. -func MustNext(ctx context.Context, name string) context.Context { +// MustExtend is Extend but panics on error. +func MustExtend(ctx context.Context, name string) context.Context { if ctx == nil { panic("context is nil") } @@ -73,8 +73,8 @@ func MustNext(ctx context.Context, name string) context.Context { return insert(ctx, c.Module(), name, c.Path()) } -// Start sets a new component on the context, resetting any existing component. -func Start(ctx context.Context, module string, name string) (context.Context, error) { +// New sets a new component on the context, resetting any existing component. +func New(ctx context.Context, module string, name string) (context.Context, error) { if ctx == nil { return nil, fmt.Errorf("context is nil") } @@ -87,8 +87,8 @@ func Start(ctx context.Context, module string, name string) (context.Context, er return insert(ctx, module, name, []string{}), nil } -// Next appends name to the current component path, inheriting its module. -func Next(ctx context.Context, name string) (context.Context, error) { +// Extend appends name to the current component path, inheriting its module. +func Extend(ctx context.Context, name string) (context.Context, error) { if ctx == nil { return nil, fmt.Errorf("context is nil") }