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.
This commit is contained in:
Caleb Thompson 2016-10-25 11:22:50 -05:00
parent c1b9cf186e
commit c063624c91
No known key found for this signature in database
GPG Key ID: 1621ADC2A0ACE70A
3 changed files with 43 additions and 25 deletions

View File

@ -18,40 +18,38 @@ var (
ErrUnaddressable = errors.New("using unaddressable value") ErrUnaddressable = errors.New("using unaddressable value")
) )
type errorsInterface interface {
GetErrors() []error
}
// Errors contains all happened errors // Errors contains all happened errors
type Errors struct { type Errors []error
errors []error
}
// GetErrors get all happened errors // GetErrors gets all happened errors
func (errs Errors) GetErrors() []error { func (errs Errors) GetErrors() []error {
return errs.errors return errs
} }
// Add add an error // Add adds an error
func (errs *Errors) Add(err error) { func (errs Errors) Add(newErrors ...error) Errors {
if errors, ok := err.(errorsInterface); ok { for _, err := range newErrors {
for _, err := range errors.GetErrors() { if errors, ok := err.(Errors); ok {
errs.Add(err) errs = errs.Add(errors...)
}
} else { } else {
for _, e := range errs.errors { ok = true
for _, e := range errs {
if err == e { if err == e {
return ok = false
} }
} }
errs.errors = append(errs.errors, err) if ok {
errs = append(errs, err)
} }
} }
}
return errs
}
// Error format happened errors // Error format happened errors
func (errs Errors) Error() string { func (errs Errors) Error() string {
var errors = []string{} var errors = []string{}
for _, e := range errs.errors { for _, e := range errs {
errors = append(errors, e.Error()) errors = append(errors, e.Error())
} }
return strings.Join(errors, "; ") return strings.Join(errors, "; ")

20
errors_test.go Normal file
View File

@ -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())
}
}

View File

@ -655,9 +655,9 @@ func (s *DB) AddError(err error) error {
s.log(err) s.log(err)
} }
errors := Errors{errors: s.GetErrors()} errors := Errors(s.GetErrors())
errors.Add(err) errors.Add(err)
if len(errors.GetErrors()) > 1 { if len(errors) > 1 {
err = errors err = errors
} }
} }
@ -669,8 +669,8 @@ func (s *DB) AddError(err error) error {
// GetErrors get happened errors from the db // GetErrors get happened errors from the db
func (s *DB) GetErrors() (errors []error) { func (s *DB) GetErrors() (errors []error) {
if errs, ok := s.Error.(errorsInterface); ok { if errs, ok := s.Error.(Errors); ok {
return errs.GetErrors() return errs
} else if s.Error != nil { } else if s.Error != nil {
return []error{s.Error} return []error{s.Error}
} }