-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathbuilder.go
178 lines (161 loc) · 4.9 KB
/
builder.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
package ns_x
import (
"github.com/bytedance/ns-x/v2/base"
"reflect"
"strconv"
"strings"
)
// Builder is a convenience tool to describe the whole network and build
// when a node is described for the first time, a unique id is assigned to it, which will be the index of node in the built network
type Builder interface {
// Chain save the current chain and begin to describe a new chain
Chain() Builder
// Node connect the given node to the end of current chain
Node(node base.Node) Builder
// Group insert a group to current chain, which means end of current chain will be connected to in node, and the end of current chain will be set to out node
Group(inName, outName string) Builder
// NodeWithName same to Node, but name it with the given name
NodeWithName(name string, node base.Node) Builder
// GroupWithName same to Group, but name the whole group with the given name
GroupWithName(name string, inName, outName string) Builder
// NodeOfName find the node with the given name, and then connect it to the end of the chain
NodeOfName(name string) Builder
// GroupOfName find the group with the given name, and then perform the Group operation on it
GroupOfName(name string) Builder
// Summary print the structure of the network to standard output
Summary() Builder
// Build actually connect the nodes with relation described before, any connection outside the builder will be overwritten
// parameters are used to configure the network, return the built network, and a map from name to named nodes
Build() (*Network, map[string]base.Node)
}
type group struct {
inName, outName string
}
type builder struct {
nodeToID map[base.Node]int
nameToNode map[string]base.Node
nodeToName map[base.Node]string
nameToGroup map[string]*group
current base.Node
connections map[base.Node]map[base.Node]interface{}
}
func NewBuilder() Builder {
return &builder{
nodeToID: map[base.Node]int{},
nameToNode: map[string]base.Node{},
nodeToName: map[base.Node]string{},
nameToGroup: map[string]*group{},
connections: map[base.Node]map[base.Node]interface{}{},
}
}
func (b *builder) Chain() Builder {
b.current = nil
return b
}
func (b *builder) Node(node base.Node) Builder {
return b.NodeWithName("", node)
}
func (b *builder) NodeWithName(name string, node base.Node) Builder {
if b.current != nil {
connection, ok := b.connections[b.current]
if !ok {
connection = map[base.Node]interface{}{}
b.connections[b.current] = connection
}
connection[node] = nil
}
if _, ok := b.nodeToID[node]; !ok {
b.nodeToID[node] = len(b.nodeToID)
}
if name != "" {
b.nameToNode[name] = node
b.nodeToName[node] = name
}
b.current = node
return b
}
func (b *builder) Group(inName, outName string) Builder {
return b.GroupWithName("", inName, outName)
}
func (b *builder) GroupWithName(name string, inName, outName string) Builder {
if name != "" {
b.nameToGroup[name] = &group{inName: inName, outName: outName}
}
in := b.requireNodeByName(inName)
out := b.requireNodeByName(outName)
b.Node(in)
b.current = out
return b
}
func (b *builder) NodeOfName(name string) Builder {
return b.Node(b.requireNodeByName(name))
}
func (b *builder) GroupOfName(name string) Builder {
group, ok := b.nameToGroup[name]
if !ok {
panic("no group with name: " + name)
}
return b.Group(group.inName, group.outName)
}
func (b *builder) Summary() Builder {
nodes := make([]base.Node, len(b.nodeToID))
println("network summary: ")
for node, index := range b.nodeToID {
nodes[index] = node
}
for index, node := range nodes {
println(b.toString(node, index))
}
println()
return b
}
func (b *builder) Build() (*Network, map[string]base.Node) {
nodes := make([]base.Node, len(b.nodeToID))
for node, index := range b.nodeToID {
nodes[index] = node
}
for node, connection := range b.connections {
node.SetNext(normalize(connection)...)
}
return NewNetwork(nodes), b.nameToNode
}
func (b *builder) toString(node base.Node, index int) string {
sb := strings.Builder{}
sb.WriteString("node ")
sb.WriteString(strconv.Itoa(index))
sb.WriteString(": {name: \"")
sb.WriteString(b.nodeToName[node])
sb.WriteString("\", type: ")
t := reflect.TypeOf(node)
if t.Kind() == reflect.Ptr {
sb.WriteString(t.Elem().Name())
} else {
sb.WriteString(t.Name())
}
sb.WriteString(", next: [")
connection := b.connections[node]
next := make([]string, 0, len(connection))
for n := range connection {
next = append(next, strconv.Itoa(b.nodeToID[n]))
}
sb.WriteString(strings.Join(next, ","))
sb.WriteString("]}")
return sb.String()
}
func normalize(nodes map[base.Node]interface{}) []base.Node {
result := make([]base.Node, 0, len(nodes))
for node := range nodes {
result = append(result, node)
}
return result
}
func (b *builder) requireNodeByName(name string) base.Node {
if name == "" {
panic("name cannot be empty string")
}
node, ok := b.nameToNode[name]
if !ok {
panic("no node with name " + name)
}
return node
}