From 408558aa0a94c336c5d229729216ae814dc72c62 Mon Sep 17 00:00:00 2001 From: Ainar Garipov Date: Thu, 16 Jan 2025 21:30:02 +0300 Subject: [PATCH] validate: add NotNilInterface --- contextutil/contextutil.go | 6 +++--- service/errorhandler.go | 11 +++-------- timeutil/schedule.go | 11 +++-------- validate/validate.go | 19 +++++++++++++++++++ validate/validate_example_test.go | 18 ++++++++++++++++++ 5 files changed, 46 insertions(+), 19 deletions(-) diff --git a/contextutil/contextutil.go b/contextutil/contextutil.go index 40ca74a..6159c74 100644 --- a/contextutil/contextutil.go +++ b/contextutil/contextutil.go @@ -44,9 +44,9 @@ func NewTimeoutConstructor(timeout time.Duration) (c *TimeoutConstructor) { // type check var _ Constructor = (*TimeoutConstructor)(nil) -// New implements the [Constructor] interface for -// *TimeoutConstructor. It returns a context with its timeout and the -// corresponding cancelation function. +// New implements the [Constructor] interface for *TimeoutConstructor. It +// returns a context with its timeout and the corresponding cancelation +// function. func (c *TimeoutConstructor) New( parent context.Context, ) (ctx context.Context, cancel context.CancelFunc) { diff --git a/service/errorhandler.go b/service/errorhandler.go index 9fa7c28..ffba585 100644 --- a/service/errorhandler.go +++ b/service/errorhandler.go @@ -50,15 +50,10 @@ type SlogErrorHandler struct { // NewSlogErrorHandler returns a new logging error handler. l and lvl must not // be nil. func NewSlogErrorHandler(l *slog.Logger, lvl slog.Leveler, msg string) (h *SlogErrorHandler) { - errs := []error{ + err := errors.Join( validate.NotNil("l", l), - } - - if lvl == nil { - errs = append(errs, fmt.Errorf("lvl: %w", errors.ErrNoValue)) - } - - err := errors.Join(errs...) + validate.NotNilInterface("lvl", lvl), + ) if err != nil { panic(fmt.Errorf("service.NewSlogErrorHandler: %w", err)) } diff --git a/timeutil/schedule.go b/timeutil/schedule.go index a8be440..e8e8c87 100644 --- a/timeutil/schedule.go +++ b/timeutil/schedule.go @@ -90,16 +90,11 @@ func NewRandomizedSchedule( minAdd time.Duration, maxAdd time.Duration, ) (s *RandomizedSchedule) { - errs := []error{ + err := errors.Join( + validate.NotNilInterface("sched", sched), validate.NotNil("r", r), validate.GreaterThan("maxAdd", maxAdd, minAdd), - } - - if sched == nil { - errs = append(errs, fmt.Errorf("sched: %w", errors.ErrNoValue)) - } - - err := errors.Join(errs...) + ) if err != nil { panic(fmt.Errorf("timeutil.NewRandomizedSchedule: %w", err)) } diff --git a/validate/validate.go b/validate/validate.go index 6cc5d30..28abae5 100644 --- a/validate/validate.go +++ b/validate/validate.go @@ -244,6 +244,25 @@ func NotNil[T any](name string, v *T) (err error) { return nil } +// NotNilInterface returns an error if v is nil. The underlying error of err is +// [errors.ErrNoValue]. +// +// For checking against emptiness (comparing with the zero value), prefer +// [NotEmpty]. +// +// NOTE: This function returns an error only if v is a nil interface value. +// This means that if v is an interface value with a type and a nil pointer, err +// is nil. +// +// TODO(a.garipov): Find ways of merging with [NotNil]. +func NotNilInterface(name string, v any) (err error) { + if v == nil { + return fmt.Errorf("%s: %w", name, errors.ErrNoValue) + } + + return nil +} + // Positive returns an error if v is less than or equal to the zero value of // type T. The underlying error of err is [errors.ErrNotPositive]. // diff --git a/validate/validate_example_test.go b/validate/validate_example_test.go index 0cfaaec..7f1f899 100644 --- a/validate/validate_example_test.go +++ b/validate/validate_example_test.go @@ -165,6 +165,24 @@ func ExampleNotNil() { // foo: no value } +func ExampleNotNilInterface() { + var v any + fmt.Println(validate.NotNilInterface("foo", v)) + + type T struct{} + v = T{} + fmt.Println(validate.NotNilInterface("foo", v)) + + // NOTE: A typed but nil interface value, be careful! + v = (*T)(nil) + fmt.Println(validate.NotNilInterface("foo", v)) + + // Output: + // foo: no value + // + // +} + func ExamplePositive() { fmt.Println(validate.Positive("foo", 1)) fmt.Println(validate.Positive("foo", 0))