]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/hcl2/hclwrite/node.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / hcl2 / hclwrite / node.go
1 package hclwrite
2
3 import (
4 "fmt"
5
6 "github.com/google/go-cmp/cmp"
7 )
8
9 // node represents a node in the AST.
10 type node struct {
11 content nodeContent
12
13 list *nodes
14 before, after *node
15 }
16
17 func newNode(c nodeContent) *node {
18 return &node{
19 content: c,
20 }
21 }
22
23 func (n *node) Equal(other *node) bool {
24 return cmp.Equal(n.content, other.content)
25 }
26
27 func (n *node) BuildTokens(to Tokens) Tokens {
28 return n.content.BuildTokens(to)
29 }
30
31 // Detach removes the receiver from the list it currently belongs to. If the
32 // node is not currently in a list, this is a no-op.
33 func (n *node) Detach() {
34 if n.list == nil {
35 return
36 }
37 if n.before != nil {
38 n.before.after = n.after
39 }
40 if n.after != nil {
41 n.after.before = n.before
42 }
43 if n.list.first == n {
44 n.list.first = n.after
45 }
46 if n.list.last == n {
47 n.list.last = n.before
48 }
49 n.list = nil
50 n.before = nil
51 n.after = nil
52 }
53
54 // ReplaceWith removes the receiver from the list it currently belongs to and
55 // inserts a new node with the given content in its place. If the node is not
56 // currently in a list, this function will panic.
57 //
58 // The return value is the newly-constructed node, containing the given content.
59 // After this function returns, the reciever is no longer attached to a list.
60 func (n *node) ReplaceWith(c nodeContent) *node {
61 if n.list == nil {
62 panic("can't replace node that is not in a list")
63 }
64
65 before := n.before
66 after := n.after
67 list := n.list
68 n.before, n.after, n.list = nil, nil, nil
69
70 nn := newNode(c)
71 nn.before = before
72 nn.after = after
73 nn.list = list
74 if before != nil {
75 before.after = nn
76 }
77 if after != nil {
78 after.before = nn
79 }
80 return nn
81 }
82
83 func (n *node) assertUnattached() {
84 if n.list != nil {
85 panic(fmt.Sprintf("attempt to attach already-attached node %#v", n))
86 }
87 }
88
89 // nodeContent is the interface type implemented by all AST content types.
90 type nodeContent interface {
91 walkChildNodes(w internalWalkFunc)
92 BuildTokens(to Tokens) Tokens
93 }
94
95 // nodes is a list of nodes.
96 type nodes struct {
97 first, last *node
98 }
99
100 func (ns *nodes) BuildTokens(to Tokens) Tokens {
101 for n := ns.first; n != nil; n = n.after {
102 to = n.BuildTokens(to)
103 }
104 return to
105 }
106
107 func (ns *nodes) Clear() {
108 ns.first = nil
109 ns.last = nil
110 }
111
112 func (ns *nodes) Append(c nodeContent) *node {
113 n := &node{
114 content: c,
115 }
116 ns.AppendNode(n)
117 n.list = ns
118 return n
119 }
120
121 func (ns *nodes) AppendNode(n *node) {
122 if ns.last != nil {
123 n.before = ns.last
124 ns.last.after = n
125 }
126 n.list = ns
127 ns.last = n
128 if ns.first == nil {
129 ns.first = n
130 }
131 }
132
133 func (ns *nodes) AppendUnstructuredTokens(tokens Tokens) *node {
134 if len(tokens) == 0 {
135 return nil
136 }
137 n := newNode(tokens)
138 ns.AppendNode(n)
139 n.list = ns
140 return n
141 }
142
143 // nodeSet is an unordered set of nodes. It is used to describe a set of nodes
144 // that all belong to the same list that have some role or characteristic
145 // in common.
146 type nodeSet map[*node]struct{}
147
148 func newNodeSet() nodeSet {
149 return make(nodeSet)
150 }
151
152 func (ns nodeSet) Has(n *node) bool {
153 if ns == nil {
154 return false
155 }
156 _, exists := ns[n]
157 return exists
158 }
159
160 func (ns nodeSet) Add(n *node) {
161 ns[n] = struct{}{}
162 }
163
164 func (ns nodeSet) Remove(n *node) {
165 delete(ns, n)
166 }
167
168 func (ns nodeSet) List() []*node {
169 if len(ns) == 0 {
170 return nil
171 }
172
173 ret := make([]*node, 0, len(ns))
174
175 // Determine which list we are working with. We assume here that all of
176 // the nodes belong to the same list, since that is part of the contract
177 // for nodeSet.
178 var list *nodes
179 for n := range ns {
180 list = n.list
181 break
182 }
183
184 // We recover the order by iterating over the whole list. This is not
185 // the most efficient way to do it, but our node lists should always be
186 // small so not worth making things more complex.
187 for n := list.first; n != nil; n = n.after {
188 if ns.Has(n) {
189 ret = append(ret, n)
190 }
191 }
192 return ret
193 }
194
195 type internalWalkFunc func(*node)
196
197 // inTree can be embedded into a content struct that has child nodes to get
198 // a standard implementation of the NodeContent interface and a record of
199 // a potential parent node.
200 type inTree struct {
201 parent *node
202 children *nodes
203 }
204
205 func newInTree() inTree {
206 return inTree{
207 children: &nodes{},
208 }
209 }
210
211 func (it *inTree) assertUnattached() {
212 if it.parent != nil {
213 panic(fmt.Sprintf("node is already attached to %T", it.parent.content))
214 }
215 }
216
217 func (it *inTree) walkChildNodes(w internalWalkFunc) {
218 for n := it.children.first; n != nil; n = n.after {
219 w(n)
220 }
221 }
222
223 func (it *inTree) BuildTokens(to Tokens) Tokens {
224 for n := it.children.first; n != nil; n = n.after {
225 to = n.BuildTokens(to)
226 }
227 return to
228 }
229
230 // leafNode can be embedded into a content struct to give it a do-nothing
231 // implementation of walkChildNodes
232 type leafNode struct {
233 }
234
235 func (n *leafNode) walkChildNodes(w internalWalkFunc) {
236 }