-
Notifications
You must be signed in to change notification settings - Fork 3
/
value.go
139 lines (110 loc) · 2.88 KB
/
value.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
135
136
137
138
139
package glut
import (
"reflect"
"strings"
"sync"
"time"
"github.com/256dpi/fire/coal"
"github.com/256dpi/fire/stick"
)
// Value is a structure used to encode a value.
type Value interface {
Validate() error
GetBase() *Base
GetAccessor(interface{}) *stick.Accessor
}
// ExtendedValue is a value that can extends its key.
type ExtendedValue interface {
Value
GetExtension() string
}
// RestrictedValue is value that can defines its deadline.
type RestrictedValue interface {
Value
GetDeadline() *time.Time
}
// Base can be embedded in a struct to turn it into a value.
type Base struct {
// The token used for value locking.
Token coal.ID
}
// GetBase implements the Value interface.
func (b *Base) GetBase() *Base {
return b
}
// GetAccessor implements the Value interface.
func (b *Base) GetAccessor(v interface{}) *stick.Accessor {
return GetMeta(v.(Value)).Accessor
}
var baseType = reflect.TypeOf(Base{})
// Meta contains meta information about a value.
type Meta struct {
// The values type.
Type reflect.Type
// The values key.
Key string
// The values time to live.
TTL time.Duration
// The used transfer coding.
Coding stick.Coding
// The accessor.
Accessor *stick.Accessor
}
var metaMutex sync.Mutex
var metaCache = map[reflect.Type]*Meta{}
// GetMeta will parse the values "glut" tag on the embedded glut.Base struct and
// return the meta object.
func GetMeta(value Value) *Meta {
// acquire mutex
metaMutex.Lock()
defer metaMutex.Unlock()
// get typ
typ := reflect.TypeOf(value)
// check cache
if meta, ok := metaCache[typ]; ok {
return meta
}
// get first field
field := typ.Elem().Field(0)
// check field type and name
if field.Type != baseType || !field.Anonymous || field.Name != "Base" {
panic(`glut: expected first struct field to be an embedded "glut.Base"`)
}
// check coding tag
json, hasJSON := field.Tag.Lookup("json")
bson, hasBSON := field.Tag.Lookup("bson")
if (hasJSON && hasBSON) || (!hasJSON && !hasBSON) {
panic(`glut: expected to find a coding tag of the form 'json:"-"' or 'bson:"-"' on "glut.Base"`)
} else if (hasJSON && json != "-") || (hasBSON && bson != "-") {
panic(`glut: expected to find a coding tag of the form 'json:"-"' or 'bson:"-"' on "glut.Base"`)
}
// get coding
coding := stick.JSON
if hasBSON {
coding = stick.BSON
}
// split tag
tag := strings.Split(field.Tag.Get("glut"), ",")
// check tag
if len(tag) != 2 || tag[0] == "" || tag[1] == "" {
panic(`glut: expected to find a tag of the form 'glut:"key,ttl"' on "glut.Base"`)
}
// get key
key := tag[0]
// get ttl
ttl, err := time.ParseDuration(tag[1])
if err != nil {
panic(`glut: invalid duration as time to live on "glut.Base"`)
}
// prepare meta
meta := &Meta{
Type: typ,
Key: key,
TTL: ttl,
Coding: coding,
Accessor: stick.BuildAccessor(value, "Base"),
}
// cache meta
metaCache[typ] = meta
return meta
}