-
Notifications
You must be signed in to change notification settings - Fork 897
/
dsl.go
214 lines (181 loc) · 4.97 KB
/
dsl.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
// Package dsl provide an API for writing gremlin dsl queries almost as-is
// in Go without using strings in the code.
//
// Note that, the API is not type-safe and assume the provided query and
// its arguments are valid.
package dsl
import (
"fmt"
"strings"
"time"
)
// Node represents a DSL step in the traversal.
type Node interface {
// Code returns the code representation of the element and its bindings (if any).
Code() (string, []interface{})
}
type (
// Token holds a simple token, like assignment.
Token string
// List represents a list of elements.
List struct {
Elements []interface{}
}
// Func represents a function call.
Func struct {
Name string
Args []interface{}
}
// Block represents a block/group of nodes.
Block struct {
Nodes []interface{}
}
// Var represents a variable assignment and usage.
Var struct {
Name string
Elem interface{}
}
)
// Code stringified the token.
func (t Token) Code() (string, []interface{}) { return string(t), nil }
// Code returns the code representation of a list.
func (l List) Code() (string, []interface{}) {
c, args := codeList(", ", l.Elements...)
return fmt.Sprintf("[%s]", c), args
}
// Code returns the code representation of a function call.
func (f Func) Code() (string, []interface{}) {
c, args := codeList(", ", f.Args...)
return fmt.Sprintf("%s(%s)", f.Name, c), args
}
// Code returns the code representation of group/block of nodes.
func (b Block) Code() (string, []interface{}) {
return codeList("; ", b.Nodes...)
}
// Code returns the code representation of variable declaration or its identifier.
func (v Var) Code() (string, []interface{}) {
c, args := code(v.Elem)
if v.Name == "" {
return c, args
}
return fmt.Sprintf("%s = %s", v.Name, c), args
}
// predefined nodes.
var (
G = Token("g")
Dot = Token(".")
)
// NewFunc returns a new function node.
func NewFunc(name string, args ...interface{}) *Func {
return &Func{Name: name, Args: args}
}
// NewList returns a new list node.
func NewList(args ...interface{}) *List {
return &List{Elements: args}
}
// Querier is the interface that wraps the Query method.
type Querier interface {
// Query returns the query-string (similar to the Gremlin byte-code) and its bindings.
Query() (string, Bindings)
}
// Bindings are used to associate a variable with a value.
type Bindings map[string]interface{}
// Add adds new value to the bindings map, formats it if needed, and returns its generated name.
func (b Bindings) Add(v interface{}) string {
k := fmt.Sprintf("$%x", len(b))
switch v := v.(type) {
case time.Time:
b[k] = v.UnixNano()
default:
b[k] = v
}
return k
}
// Cardinality of vertex properties.
type Cardinality string
// Cardinality options.
const (
Set Cardinality = "set"
Single Cardinality = "single"
)
// Code implements the Node interface.
func (c Cardinality) Code() (string, []interface{}) { return string(c), nil }
// Keyword defines a Gremlin keyword.
type Keyword string
// Keyword options.
const (
ID Keyword = "id"
)
// Code implements the Node interface.
func (k Keyword) Code() (string, []interface{}) { return string(k), nil }
// Order of vertex properties.
type Order string
// Order options.
const (
Incr Order = "incr"
Decr Order = "decr"
Shuffle Order = "shuffle"
)
// Code implements the Node interface.
func (o Order) Code() (string, []interface{}) { return string(o), nil }
// Column references a particular type of column in a complex data structure such as a Map, a Map.Entry, or a Path.
type Column string
// Column options.
const (
Keys Column = "keys"
Values Column = "values"
)
// Code implements the Node interface.
func (o Column) Code() (string, []interface{}) { return string(o), nil }
// Scope used for steps that have a variable scope which alter the manner in which the step will behave in relation to how the traverses are processed.
type Scope string
// Scope options.
const (
Local Scope = "local"
Global Scope = "global"
)
// Code implements the Node interface.
func (s Scope) Code() (string, []interface{}) { return string(s), nil }
func codeList(sep string, vs ...interface{}) (string, []interface{}) {
var (
br strings.Builder
args []interface{}
)
for i, node := range vs {
if i > 0 {
br.WriteString(sep)
}
c, nargs := code(node)
br.WriteString(c)
args = append(args, nargs...)
}
return br.String(), args
}
func code(v interface{}) (string, []interface{}) {
switch n := v.(type) {
case Node:
return n.Code()
case *Traversal:
var (
b strings.Builder
args []interface{}
)
for i := range n.nodes {
code, nargs := n.nodes[i].Code()
b.WriteString(code)
args = append(args, nargs...)
}
return b.String(), args
default:
return "%s", []interface{}{v}
}
}
func sface(args []string) (v []interface{}) {
for _, s := range args {
v = append(v, s)
}
return
}