/
target.go
146 lines (130 loc) · 4.29 KB
/
target.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
140
141
142
143
144
145
146
package query
// TargetType represents a query target type, which is a source of information
// that a query might require, such as metadata, structured message contents,
// the context, etc.
type TargetType int
// TargetTypes
const (
TargetMetadata TargetType = iota
TargetValue
TargetRoot
TargetVariable
)
// TargetPath represents a target type and segmented path that a query function
// references. An empty path indicates the root of the type is targetted.
type TargetPath struct {
Type TargetType
Path []string
}
// NewTargetPath constructs a new target path from a type and zero or more path
// segments.
func NewTargetPath(t TargetType, path ...string) TargetPath {
return TargetPath{
Type: t,
Path: path,
}
}
func aggregateTargetPaths(fns ...Function) func(ctx TargetsContext) (TargetsContext, []TargetPath) {
return func(ctx TargetsContext) (TargetsContext, []TargetPath) {
var paths []TargetPath
for _, fn := range fns {
if fn != nil {
_, tmpPaths := fn.QueryTargets(ctx)
paths = append(paths, tmpPaths...)
}
}
return ctx, paths
}
}
// TargetsContext describes the current Bloblang execution environment from the
// perspective of a particular query function in a way that allows it to
// determine which values it is targetting and the origins of those values.
//
// The environment consists of named maps that are globally accessible, the
// current value that is being executed upon by methods (when applicable), the
// general (main) context (referenced by the keyword `this`) and any other named
// contexts accessible at this point.
//
// Since it's possible for any query function to reference and return multiple
// target candidates (match expressions, etc) then each context and the current
// value are lists of paths, each being a candidate at runtime.
type TargetsContext struct {
Maps map[string]Function
currentValues []TargetPath
mainContext []TargetPath
prevContext *prevContextPath
namedContext *namedContextPath
}
type prevContextPath struct {
paths []TargetPath
next *prevContextPath
}
type namedContextPath struct {
name string
paths []TargetPath
next *namedContextPath
}
// Value returns the current value of the targets context, which is the path(s)
// being executed upon by methods.
func (ctx TargetsContext) Value() []TargetPath {
return ctx.currentValues
}
// MainContext returns the path of the main context.
func (ctx TargetsContext) MainContext() []TargetPath {
return ctx.mainContext
}
// NamedContext returns the path of a named context if it exists.
func (ctx TargetsContext) NamedContext(name string) []TargetPath {
current := ctx.namedContext
for current != nil {
if current.name == name {
return current.paths
}
current = current.next
}
return nil
}
// WithValues returns a targets context where the current value being executed
// upon by methods is set to something new.
func (ctx TargetsContext) WithValues(paths []TargetPath) TargetsContext {
ctx.currentValues = paths
return ctx
}
// WithValuesAsContext returns a targets context where the current value being
// executed upon by methods is now the main context. This happens when a query
// function is executed as a method, or within branches of match expressions.
func (ctx TargetsContext) WithValuesAsContext() TargetsContext {
ctx.prevContext = &prevContextPath{
paths: ctx.mainContext,
next: ctx.prevContext,
}
ctx.mainContext = ctx.currentValues
ctx.currentValues = nil
return ctx
}
// WithContextAsNamed moves the latest context into a named context and returns
// the context prior to that one to the main context. This is a way for named
// context mappings to correct the contexts so that the child query function
// returns the right paths.
func (ctx TargetsContext) WithContextAsNamed(name string) TargetsContext {
previous := ctx.namedContext
ctx.namedContext = &namedContextPath{
name: name,
paths: ctx.mainContext,
next: previous,
}
if ctx.prevContext != nil {
ctx.mainContext = ctx.prevContext.paths
ctx.prevContext = ctx.prevContext.next
}
return ctx
}
// PopContext returns a targets context with the latest context dropped and the
// previous (when applicable) returned.
func (ctx TargetsContext) PopContext() TargetsContext {
if ctx.prevContext != nil {
ctx.mainContext = ctx.prevContext.paths
ctx.prevContext = ctx.prevContext.next
}
return ctx
}