-
Notifications
You must be signed in to change notification settings - Fork 3
/
job.go
137 lines (108 loc) · 2.76 KB
/
job.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
package axe
import (
"reflect"
"strings"
"sync"
"github.com/256dpi/fire/coal"
"github.com/256dpi/fire/stick"
)
// Job is a structure used to encode a job.
type Job interface {
ID() coal.ID
Validate() error
GetBase() *Base
GetAccessor(interface{}) *stick.Accessor
}
// Base can be embedded in a struct to turn it into a job.
type Base struct {
// The id of the document.
DocID coal.ID
// The label of the job.
Label string
}
// B is a short-hand to construct a base with a label.
func B(label string) Base {
return Base{
Label: label,
}
}
// ID will return the job id.
func (b *Base) ID() coal.ID {
return b.DocID
}
// GetBase implements the Job interface.
func (b *Base) GetBase() *Base {
return b
}
// GetAccessor implements the Model interface.
func (b *Base) GetAccessor(v interface{}) *stick.Accessor {
return GetMeta(v.(Job)).Accessor
}
var baseType = reflect.TypeOf(Base{})
// Meta contains meta information about a job.
type Meta struct {
// The jobs type.
Type reflect.Type
// The jobs name.
Name string
// 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 jobs "axe" tag on the embedded axe.Base struct and
// return the meta object.
func GetMeta(job Job) *Meta {
// acquire mutex
metaMutex.Lock()
defer metaMutex.Unlock()
// get typ
typ := reflect.TypeOf(job).Elem()
// check cache
if meta, ok := metaCache[typ]; ok {
return meta
}
// get first field
field := typ.Field(0)
// check field type and name
if field.Type != baseType || !field.Anonymous || field.Name != "Base" {
panic(`axe: expected first struct field to be an embedded "axe.Base"`)
}
// check coding tag
json, hasJSON := field.Tag.Lookup("json")
bson, hasBSON := field.Tag.Lookup("bson")
if (hasJSON && hasBSON) || (!hasJSON && !hasBSON) {
panic(`axe: expected to find a coding tag of the form 'json:"-"' or 'bson:"-"' on "axe.Base"`)
} else if (hasJSON && json != "-") || (hasBSON && bson != "-") {
panic(`axe: expected to find a coding tag of the form 'json:"-"' or 'bson:"-"' on "axe.Base"`)
}
// get coding
coding := stick.JSON
if hasBSON {
coding = stick.BSON
}
// split tag
tag := strings.Split(field.Tag.Get("axe"), ",")
// check tag
if len(tag) != 1 || tag[0] == "" {
panic(`axe: expected to find a tag of the form 'axe:"name"' on "axe.Base"`)
}
// get name
name := tag[0]
// prepare meta
meta := &Meta{
Type: typ,
Name: name,
Coding: coding,
Accessor: stick.BuildAccessor(job, "Base"),
}
// cache meta
metaCache[typ] = meta
return meta
}
// Make returns a pointer to a new zero initialized job e.g. *Increment.
func (m *Meta) Make() Job {
return reflect.New(m.Type).Interface().(Job)
}