diff --git a/README.md b/README.md index dc42ec5..55030ad 100644 --- a/README.md +++ b/README.md @@ -76,12 +76,14 @@ At the connection layer, another `MustExtend` call extends the path to ### Usage Notes -**Error-Returning vs Panic Variants**: +**Function Variants**: - Use `New` and `Extend` when a missing or invalid component is a recoverable condition. - Use `MustNew` and `MustExtend` at library boundaries where a missing component is a programming error that should halt execution immediately. +- Use `TryExtend` if you only want to extend a component if it exists, +otherwise use the parent context as-is. **Generic Output**: diff --git a/main.go b/main.go index f35c2fa..e32fd7e 100644 --- a/main.go +++ b/main.go @@ -93,6 +93,21 @@ func Extend(ctx context.Context, name string) (context.Context, error) { return insert(ctx, c.Module(), name, c.Path()), nil } +// TryExtend returns ctx if no parent component is attached, otherwise +// extends the existing component path. +func TryExtend(ctx context.Context, name string) (context.Context, error) { + if ctx == nil { + return nil, fmt.Errorf("context is nil") + } + + c, ok := Get(ctx) + if !ok || name == "" { + return ctx, nil + } + + return insert(ctx, c.Module(), name, c.Path()), nil +} + // MustNew is New but panics on error. func MustNew(ctx context.Context, module string, name string) context.Context { if ctx == nil { diff --git a/main_test.go b/main_test.go index e1f7556..8d57834 100644 --- a/main_test.go +++ b/main_test.go @@ -80,6 +80,17 @@ func TestExtend(t *testing.T) { } }) + t.Run("try passes through", func(t *testing.T) { + inCtx := context.Background() + outCtx, err := TryExtend(inCtx, "subcomponent") + if err != nil { + t.Fatal("unexpected error:", err) + } + if outCtx != inCtx { + t.Errorf("expected context to pass through") + } + }) + t.Run("must variant should panic", func(t *testing.T) { assertPanics(t, func() { MustExtend(context.Background(), "subcomponent")