-
Notifications
You must be signed in to change notification settings - Fork 2
/
proto_hash.go
108 lines (86 loc) · 2.56 KB
/
proto_hash.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package redis
import (
"context"
"reflect"
"github.com/go-redis/redis"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
"libs.altipla.consulting/errors"
)
type ProtoHash struct {
db *Database
key string
}
func (hash *ProtoHash) PrepareInsert() *ProtoHashInsert {
return &ProtoHashInsert{
hash: hash,
fields: make(map[string]interface{}),
}
}
// GetMulti fetchs a list of keys from the hash. Result should be a slice of proto.Message
// that will be filled with the results in the same order as the keys.
func (hash *ProtoHash) GetMulti(ctx context.Context, keys []string, result interface{}) error {
if len(keys) == 0 {
return nil
}
rt := reflect.TypeOf(result)
rv := reflect.ValueOf(result)
msg := reflect.TypeOf((*proto.Message)(nil)).Elem()
if rt.Kind() != reflect.Ptr || rt.Elem().Kind() != reflect.Slice || !rt.Elem().Elem().Implements(msg) {
return errors.Errorf("expected a pointer to a slice for the result, received %T", result)
}
dest := reflect.MakeSlice(rt.Elem(), 0, 0)
var merr MultiError
redisResult, err := hash.db.Cmdable(ctx).HMGet(hash.key, keys...).Result()
if err != nil {
return errors.Trace(err)
}
for _, item := range redisResult {
var model reflect.Value
if item == nil {
model = reflect.Zero(rt.Elem().Elem())
merr = append(merr, ErrNoSuchEntity)
} else {
model = reflect.New(rt.Elem().Elem().Elem())
if err := unmarshalProto(item.(string), model.Interface().(proto.Message)); err != nil {
return errors.Trace(err)
}
merr = append(merr, nil)
}
dest = reflect.Append(dest, model)
}
rv.Elem().Set(dest)
if merr.HasError() {
return merr
}
return nil
}
func (hash *ProtoHash) Get(ctx context.Context, key string, model proto.Message) error {
redisResult, err := hash.db.Cmdable(ctx).HGet(hash.key, key).Result()
if err != nil {
if err == redis.Nil {
return ErrNoSuchEntity
}
return errors.Trace(err)
}
return unmarshalProto(redisResult, model)
}
func (hash *ProtoHash) Delete(ctx context.Context, key string) error {
return hash.db.Cmdable(ctx).HDel(hash.key, key).Err()
}
type ProtoHashInsert struct {
hash *ProtoHash
fields map[string]interface{}
}
func (insert *ProtoHashInsert) Set(ctx context.Context, key string, value proto.Message) error {
m := new(jsonpb.Marshaler)
encoded, err := m.MarshalToString(value)
if err != nil {
return errors.Trace(err)
}
insert.fields[key] = encoded
return nil
}
func (insert *ProtoHashInsert) Commit(ctx context.Context) error {
return insert.hash.db.Cmdable(ctx).HMSet(insert.hash.key, insert.fields).Err()
}