-
Notifications
You must be signed in to change notification settings - Fork 2
/
hash.go
134 lines (111 loc) · 3.14 KB
/
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package redis
import (
"context"
"reflect"
"strconv"
"time"
"libs.altipla.consulting/collections"
"libs.altipla.consulting/errors"
)
type Hash struct {
db *Database
name string
props []*property
}
type MaskOpt []string
func Mask(cols ...string) []string {
return cols
}
func (hash *Hash) Get(ctx context.Context, key string, instance Model, masks ...MaskOpt) error {
var included []string
for _, mask := range masks {
included = append(included, mask...)
}
modelProps := updatedProps(hash.props, instance)
var names []string
var filteredProps []*property
for _, prop := range modelProps {
if len(included) > 0 && !collections.HasString(included, prop.Name) {
continue
}
names = append(names, prop.Name)
filteredProps = append(filteredProps, prop)
}
fields, err := hash.db.Cmdable(ctx).HMGet(hash.name+":"+key, names...).Result()
if err != nil {
return errors.Wrapf(err, "cannot get fields of hash")
}
var nilFields int
for i, field := range fields {
prop := filteredProps[i]
if field == nil {
nilFields++
continue
}
switch prop.Value.(type) {
case int64:
n, err := strconv.ParseInt(field.(string), 10, 64)
if err != nil {
return errors.Wrapf(err, "cannot parse int64 field (%s = %s)", prop.Name, field)
}
prop.ReflectValue.Set(reflect.ValueOf(n))
case string:
prop.ReflectValue.Set(reflect.ValueOf(field.(string)))
case time.Time:
var t time.Time
if err := t.UnmarshalText([]byte(field.(string))); err != nil {
return errors.Wrapf(err, "cannot unmarshal time field (%s = %s)", prop.Name, field)
}
prop.ReflectValue.Set(reflect.ValueOf(t))
}
}
if nilFields == len(filteredProps) {
return ErrNoSuchEntity
}
return nil
}
func (hash *Hash) Put(ctx context.Context, key string, instance Model, masks ...MaskOpt) error {
var included []string
for _, mask := range masks {
included = append(included, mask...)
}
modelProps := updatedProps(hash.props, instance)
fields := map[string]interface{}{}
for _, prop := range modelProps {
if len(included) > 0 && !collections.HasString(included, prop.Name) {
continue
}
var store string
switch v := prop.Value.(type) {
case int64:
store = strconv.FormatInt(v, 10)
case string:
store = v
case time.Time:
t, err := v.MarshalText()
if err != nil {
return errors.Wrapf(err, "cannot marshal time (%s = %s)", prop.Name, prop.Value)
}
store = string(t)
}
fields[prop.Name] = store
}
if err := hash.db.Cmdable(ctx).HMSet(hash.name+":"+key, fields).Err(); err != nil {
return errors.Wrapf(err, "cannot set fields of hash")
}
return nil
}
// Delete inmediately removes the key from the hash.
func (hash *Hash) Delete(ctx context.Context, key string) error {
if err := hash.db.Cmdable(ctx).Del(hash.name + ":" + key).Err(); err != nil {
return errors.Wrapf(err, "cannot delete hash %s", key)
}
return nil
}
// ExpireAt sets the expiration of a key of this hash.
func (hash *Hash) ExpireAt(ctx context.Context, key string, t time.Time) error {
if err := hash.db.Cmdable(ctx).ExpireAt(hash.name+":"+key, t).Err(); err != nil {
return errors.Wrapf(err, "cannot expire hash %s", key)
}
return nil
}