Skip to content

Commit

Permalink
feat(context): adding lazy evaluated value
Browse files Browse the repository at this point in the history
  • Loading branch information
samber committed Aug 21, 2023
1 parent 2a9a9c4 commit 9c1d3f1
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 17 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ The `oops.OopsError` builder must finish with either `.Errorf(...)`, `.Wrap(...)

| Builder method | Getter | Description |
| --------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `.With(string, any)` | `err.Context() map[string]any` | Supply a list of attributes key+value |
| `.With(string, any)` | `err.Context() map[string]any` | Supply a list of attributes key+value. Values of type `func() any {}` are accepted and evaluated lazily. |
| `.Code(string)` | `err.Code() string` | Set a code or slug that describes the error. Error messages are intented to be read by humans, but such code is expected to be read by machines and be transported over different services |
| `.Time(time.Time)` | `err.Time() time.Time` | Set the error time (default: `time.Now()`) |
| `.Since(time.Time)` | `err.Duration() time.Duration` | Set the error duration |
Expand Down Expand Up @@ -255,6 +255,7 @@ err3 := oops.
With("driver", "postgresql").
With("query", query).
With("query.duration", queryDuration).
With("lorem", func() string { return "ipsum" }). // lazy evaluation
Errorf("could not fetch user")

// with trace+span
Expand Down
36 changes: 21 additions & 15 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,13 @@ func (o OopsError) Tags() []string {

// Context returns a k/v context of the error.
func (o OopsError) Context() map[string]any {
return mergeNestedErrorMap(
o,
func(e OopsError) map[string]any {
return e.context
},
return lazyMapEvaluation(
mergeNestedErrorMap(
o,
func(e OopsError) map[string]any {
return e.context
},
),
)
}

Expand Down Expand Up @@ -180,11 +182,13 @@ func (o OopsError) User() (string, map[string]any) {
return e.userID
},
)
userData := mergeNestedErrorMap(
o,
func(e OopsError) map[string]any {
return e.userData
},
userData := lazyMapEvaluation(
mergeNestedErrorMap(
o,
func(e OopsError) map[string]any {
return e.userData
},
),
)

return userID, userData
Expand All @@ -198,11 +202,13 @@ func (o OopsError) Tenant() (string, map[string]any) {
return e.tenantID
},
)
tenantData := mergeNestedErrorMap(
o,
func(e OopsError) map[string]any {
return e.tenantData
},
tenantData := lazyMapEvaluation(
mergeNestedErrorMap(
o,
func(e OopsError) map[string]any {
return e.tenantData
},
),
)

return tenantID, tenantData
Expand Down
2 changes: 1 addition & 1 deletion examples/log/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func d() error {
In("authz").
Time(time.Now()).
With("user_id", 1234).
With("permission", "post.create").
With("permission", func() any { return "post.create" }). // lazy evaluation
Hint("Runbook: https://doc.acme.org/doc/abcd.md").
User("user-123", "firstname", "john", "lastname", "doe").
Errorf("permission denied")
Expand Down
1 change: 1 addition & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
30 changes: 30 additions & 0 deletions kv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package oops

import (
"reflect"
)

func lazyMapEvaluation(data map[string]any) map[string]any {
for key, value := range data {
switch v := value.(type) {
case map[string]any:
data[key] = lazyMapEvaluation(v)
default:
data[key] = lazyValueEvaluation(value)
}
}

return data
}
func lazyValueEvaluation(value any) any {
v := reflect.ValueOf(value)
if !v.IsValid() || v.Kind() != reflect.Func {
return value
}

if v.Type().NumIn() != 0 || v.Type().NumOut() != 1 {
return value
}

return v.Call([]reflect.Value{})[0].Interface()
}
9 changes: 9 additions & 0 deletions oops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ func TestOopsWith(t *testing.T) {
is.Equal(err.(OopsError).context, map[string]any{"user_id": 1234, "foo": "bar"})
}

func TestOopsWithLazyEvaluation(t *testing.T) {
is := assert.New(t)

// lazy evaluation
err := new().With("user_id", func() int { return 1234 }, "foo", map[string]any{"bar": func() string { return "baz" }}).Wrap(assert.AnError)
is.Error(err)
is.Equal(err.(OopsError).Context(), map[string]any{"user_id": 1234, "foo": map[string]any{"bar": "baz"}})
}

func TestOopsHint(t *testing.T) {
is := assert.New(t)

Expand Down

0 comments on commit 9c1d3f1

Please sign in to comment.