From c063624c91912c7c150dd4d9d1fe6a667c573819 Mon Sep 17 00:00:00 2001 From: Caleb Thompson <caleb@calebthompson.io> Date: Tue, 25 Oct 2016 11:22:50 -0500 Subject: [PATCH] Make gorm.Errors available for use outside gorm gorm.Errors, which usefully implements `error` for an `[]error` as returned by `DB.GetError()` was already exported, but because it used a private field `errors`, it was not able to be created due to the compile-time error: implicit assignment of unexported field 'errors' in gorm.Errors literal The trivial solution would be to export the `errors` field on `gorm.Errors`, but this led to the issue that the common pattern of checking `err != nil` failed because a struct{error: nil} != nil. We can take advantage of type aliasing here to make Errors an []error, which can in fact be nil and would pass `err != nil` on the happy path. * Remove `(Errors) GetErrors()`, as it's less useful when Errors is an []error which can be iterated over. While this is technically a breaking change, we never expose an Errors and its difficult to build one (it can be done with the existing `(Errors) Add(error)`), but awkwardly. This removal can be reverted without issue and we can make it an identity method, but it seemed an opportune time to reduce API surface area on something that likely isn't used. * Remove errorsInterface, as it's not useful without `(Errors) GetErrors()` * Change `(*Errors) Add(error)` => `(Errors) Add(error...) Errors` because we can't modify even a *Errors when it's a type alias. This is more idiomatic as it follows the pattern of `slice = append(slice, element)` Go developers are familiar with. --- errors.go | 40 +++++++++++++++++++--------------------- errors_test.go | 20 ++++++++++++++++++++ main.go | 8 ++++---- 3 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 errors_test.go diff --git a/errors.go b/errors.go index ce3a25c0..832fa9b0 100644 --- a/errors.go +++ b/errors.go @@ -18,40 +18,38 @@ var ( ErrUnaddressable = errors.New("using unaddressable value") ) -type errorsInterface interface { - GetErrors() []error -} - // Errors contains all happened errors -type Errors struct { - errors []error -} +type Errors []error -// GetErrors get all happened errors +// GetErrors gets all happened errors func (errs Errors) GetErrors() []error { - return errs.errors + return errs } -// Add add an error -func (errs *Errors) Add(err error) { - if errors, ok := err.(errorsInterface); ok { - for _, err := range errors.GetErrors() { - errs.Add(err) - } - } else { - for _, e := range errs.errors { - if err == e { - return +// Add adds an error +func (errs Errors) Add(newErrors ...error) Errors { + for _, err := range newErrors { + if errors, ok := err.(Errors); ok { + errs = errs.Add(errors...) + } else { + ok = true + for _, e := range errs { + if err == e { + ok = false + } + } + if ok { + errs = append(errs, err) } } - errs.errors = append(errs.errors, err) } + return errs } // Error format happened errors func (errs Errors) Error() string { var errors = []string{} - for _, e := range errs.errors { + for _, e := range errs { errors = append(errors, e.Error()) } return strings.Join(errors, "; ") diff --git a/errors_test.go b/errors_test.go new file mode 100644 index 00000000..9a428dec --- /dev/null +++ b/errors_test.go @@ -0,0 +1,20 @@ +package gorm_test + +import ( + "errors" + "testing" + + "github.com/jinzhu/gorm" +) + +func TestErrorsCanBeUsedOutsideGorm(t *testing.T) { + errs := []error{errors.New("First"), errors.New("Second")} + + gErrs := gorm.Errors(errs) + gErrs = gErrs.Add(errors.New("Third")) + gErrs = gErrs.Add(gErrs) + + if gErrs.Error() != "First; Second; Third" { + t.Fatalf("Gave wrong error, got %s", gErrs.Error()) + } +} diff --git a/main.go b/main.go index e4af5873..192dbd7c 100644 --- a/main.go +++ b/main.go @@ -655,9 +655,9 @@ func (s *DB) AddError(err error) error { s.log(err) } - errors := Errors{errors: s.GetErrors()} + errors := Errors(s.GetErrors()) errors.Add(err) - if len(errors.GetErrors()) > 1 { + if len(errors) > 1 { err = errors } } @@ -669,8 +669,8 @@ func (s *DB) AddError(err error) error { // GetErrors get happened errors from the db func (s *DB) GetErrors() (errors []error) { - if errs, ok := s.Error.(errorsInterface); ok { - return errs.GetErrors() + if errs, ok := s.Error.(Errors); ok { + return errs } else if s.Error != nil { return []error{s.Error} }