diff --git a/koboldsim/stats.go b/koboldsim/stats.go index 338eaf8..7ebc746 100644 --- a/koboldsim/stats.go +++ b/koboldsim/stats.go @@ -220,7 +220,7 @@ var ErrFieldSetPanic = fmt.Errorf("%w: panic when setting", ErrBadFieldLabel) // Use a FieldLabel to add an amount to the matching field. If no such // field can be found, the field is not exported, or the field is not // of type float64, this returns an error ad does not change any values. -func (k *KoboldMine) Add(which FieldLabel, amount float64) (finalErr error) { +func (k *KoboldMine) Add(which FieldLabel, amount float64) error { kv := reflect.ValueOf(k).Elem() f := kv.FieldByName(string(which)) if !f.IsValid() { @@ -230,15 +230,8 @@ func (k *KoboldMine) Add(which FieldLabel, amount float64) (finalErr error) { return fmt.Errorf("cannot add %f to field %q: %w", amount, which, ErrFieldNotFloat) } - defer func() { - if r := recover(); r != nil { - if e, ok := r.(error); ok { - finalErr = fmt.Errorf("could not add %f to field %q: %w: %w", amount, which, ErrFieldSetPanic, e) - } else { - finalErr = fmt.Errorf("could not add %f to field %q: %w: %v", amount, which, ErrFieldSetPanic, r) - } - } - }() - f.SetFloat(f.Float() + amount) + if err := try(func() { f.SetFloat(f.Float() + amount) }); err != nil { + return fmt.Errorf("could not add %f to field %q: %w", amount, which, err) + } return nil } diff --git a/koboldsim/util.go b/koboldsim/util.go index 0e36564..5e1023f 100644 --- a/koboldsim/util.go +++ b/koboldsim/util.go @@ -1,6 +1,8 @@ package koboldsim import ( + "errors" + "fmt" "math" "golang.org/x/exp/constraints" @@ -47,3 +49,23 @@ func clamp[T constraints.Ordered](a, b, c T) T { // `a` is neither most nor least; therefore, `a` is mid return a } + +var ErrWrappedPanic = errors.New("panic") + +// try catches a panic in the provided func and demotes it to an error, if any +// panic occurs. The returned error, if any, wraps `ErrWrappedPanic`. If the +// panic argument is itself an error, it is also wrapped; otherwise, it is +// stringified into the error message using `%v`. +func try(f func()) (finalErr error) { + defer func() { + if r := recover(); r != nil { + if e, ok := r.(error); ok { + finalErr = fmt.Errorf("%w: %w", ErrWrappedPanic, e) + return + } + finalErr = fmt.Errorf("%w: %v", ErrWrappedPanic, r) + } + }() + f() + return nil +}