Skip to content

Commit

Permalink
Hashable can return an error.
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellh committed Nov 22, 2020
1 parent 26faa52 commit 7791672
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 52 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ sending data across the network, caching values locally (de-dup), and so on.

* Optionally, specify a custom hash function to optimize for speed, collision
avoidance for your data set, etc.

* Optionally, hash the output of `.String()` on structs that implement fmt.Stringer,
allowing effective hashing of time.Time

* Optionally, override the hashing process with a `hash` field or by implementing `hashstructure.Hashable`.
* Optionally, override the hashing process by implementing `Hashable`.

## Installation

Expand Down
6 changes: 1 addition & 5 deletions hashstructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,8 @@ func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) {
include = impl
}

if f := v.FieldByName("hash"); f.IsValid() {
return f.Uint(), nil
}

if impl, ok := parent.(Hashable); ok {
return impl.Hash(), nil
return impl.Hash()
}

t := v.Type()
Expand Down
75 changes: 32 additions & 43 deletions hashstructure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,46 +125,6 @@ func TestHash_equal(t *testing.T) {
true,
},

{
struct {
Foo string
unexported string
hash uint64
}{
Foo: "bar",
unexported: "baz",
hash: 10,
},
struct {
Foo string
unexported string
hash uint64
}{
Foo: "bar",
unexported: "bang",
hash: 20,
},
false,
},

{
struct {
Foo string
hash uint64
}{
Foo: "bar",
hash: 10,
},
struct {
Foo string
hash uint64
}{
Foo: "baz",
hash: 10,
},
true,
},

{
struct {
testFoo
Expand Down Expand Up @@ -584,35 +544,58 @@ func TestHash_hashable(t *testing.T) {
cases := []struct {
One, Two interface{}
Match bool
Err string
}{
{
testHashable{Value: "foo"},
testHashable{Value: "foo"},
true,
"",
},

{
testHashable{Value: "foo1"},
testHashable{Value: "foo2"},
true,
"",
},
{
testHashable{Value: "foo"},
testHashable{Value: "bar"},
false,
"",
},
{
testHashable{Value: "nofoo"},
testHashable{Value: "bar"},
true,
"",
},
{
testHashable{Value: "bar", Err: fmt.Errorf("oh no")},
testHashable{Value: "bar"},
true,
"oh no",
},
}

for _, tc := range cases {
one, err := Hash(tc.One, nil)
if tc.Err != "" {
if err == nil {
t.Fatal("expected error")
}

if !strings.Contains(err.Error(), tc.Err) {
t.Fatalf("expected error to contain %q, got: %s", tc.Err, err)
}

return
}
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}

two, err := Hash(tc.Two, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
Expand Down Expand Up @@ -657,11 +640,17 @@ func (t testIncludableMap) HashIncludeMap(field string, k, v interface{}) (bool,

type testHashable struct {
Value string
Err error
}

func (t testHashable) Hash() uint64 {
func (t testHashable) Hash() (uint64, error) {
if t.Err != nil {
return 0, t.Err
}

if strings.HasPrefix(t.Value, "foo") {
return 500
return 500, nil
}
return 100

return 100, nil
}
6 changes: 4 additions & 2 deletions include.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ type IncludableMap interface {
HashIncludeMap(field string, k, v interface{}) (bool, error)
}

// Hashable is an interface that can optionally be implemented by a struct to override the hash generation.
// Hashable is an interface that can optionally be implemented by a struct
// to override the hash value. This value will override the hash value for
// the entire struct. Entries in the struct will not be hashed.
type Hashable interface {
Hash() uint64
Hash() (uint64, error)
}

0 comments on commit 7791672

Please sign in to comment.