| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: BUSL-1.1 |
| |
| package stackaddrs |
| |
| import ( |
| "strings" |
| |
| "github.com/hashicorp/terraform/internal/addrs" |
| "github.com/hashicorp/terraform/internal/collections" |
| ) |
| |
| // Stack represents the address of a stack within the tree of stacks. |
| // |
| // The root stack [RootStack] represents the top-level stack and then any |
| // other value of this type represents an embedded stack descending from it. |
| type Stack []StackStep |
| |
| type StackStep struct { |
| Name string |
| } |
| |
| var RootStack Stack |
| |
| // IsRoot returns true if this object represents the root stack, or false |
| // otherwise. |
| func (s Stack) IsRoot() bool { |
| return len(s) == 0 |
| } |
| |
| // Parent returns the parent of the reciever, or panics if the receiver is |
| // representing the root stack. |
| func (s Stack) Parent() Stack { |
| newLen := len(s) - 1 |
| if newLen < 0 { |
| panic("root stack has no parent") |
| } |
| return s[:newLen:newLen] |
| } |
| |
| // Child constructs the address of an embedded stack that's a child of the |
| // receiver. |
| func (s Stack) Child(name string) Stack { |
| ret := make([]StackStep, len(s), len(s)+1) |
| copy(ret, s) |
| return append(ret, StackStep{name}) |
| } |
| |
| func (s Stack) String() string { |
| if s.IsRoot() { |
| // Callers should typically not ask for the string representation of |
| // the main root stack, but we'll return a reasonable placeholder |
| // for situations like e.g. internal logs where we just fmt %s in an |
| // arbitrary stack address that is sometimes the main stack. |
| return "<main>" |
| } |
| var buf strings.Builder |
| for i, step := range s { |
| if i != 0 { |
| buf.WriteByte('.') |
| } |
| buf.WriteString("stack.") |
| buf.WriteString(step.Name) |
| } |
| return buf.String() |
| } |
| |
| func (s Stack) UniqueKey() collections.UniqueKey[Stack] { |
| return stackUniqueKey(s.String()) |
| } |
| |
| type stackUniqueKey string |
| |
| // IsUniqueKey implements collections.UniqueKey. |
| func (stackUniqueKey) IsUniqueKey(Stack) {} |
| |
| // StackInstance represents the address of an instance of a stack within |
| // the tree of stacks. |
| // |
| // [RootStackInstance] represents the singleton instance of the top-level stack |
| // and then any other value of this type represents an instance of an embedded |
| // stack descending from it. |
| type StackInstance []StackInstanceStep |
| |
| type StackInstanceStep struct { |
| Name string |
| Key addrs.InstanceKey |
| } |
| |
| var RootStackInstance StackInstance |
| |
| // IsRoot returns true if this object represents the singleton instance of the |
| // root stack, or false otherwise. |
| func (s StackInstance) IsRoot() bool { |
| return len(s) == 0 |
| } |
| |
| // Parent returns the parent of the reciever, or panics if the receiver is |
| // representing the root stack. |
| func (s StackInstance) Parent() StackInstance { |
| newLen := len(s) - 1 |
| if newLen < 0 { |
| panic("root stack has no parent") |
| } |
| return s[:newLen:newLen] |
| } |
| |
| // Child constructs the address of an embedded stack that's a child of the |
| // receiver. |
| func (s StackInstance) Child(name string, key addrs.InstanceKey) StackInstance { |
| ret := make([]StackInstanceStep, len(s), len(s)+1) |
| copy(ret, s) |
| return append(ret, StackInstanceStep{ |
| Name: name, |
| Key: key, |
| }) |
| } |
| |
| // Call returns the address of the embedded stack call that the receiever |
| // belongs to, or panics if the receiver is the root module since the root |
| // module is called only implicitly. |
| func (s StackInstance) Call() AbsStackCall { |
| last := s[len(s)-1] |
| si := s[: len(s)-1 : len(s)-1] |
| return AbsStackCall{ |
| Stack: si, |
| Item: StackCall{ |
| Name: last.Name, |
| }, |
| } |
| } |
| |
| // ConfigAddr returns the [Stack] corresponding to the receiving [StackInstance]. |
| func (s StackInstance) ConfigAddr() Stack { |
| if s.IsRoot() { |
| return RootStack |
| } |
| ret := make(Stack, len(s)) |
| for i, step := range s { |
| ret[i] = StackStep{Name: step.Name} |
| } |
| return ret |
| } |
| |
| func (s StackInstance) String() string { |
| if s.IsRoot() { |
| // Callers should typically not ask for the string representation of |
| // the main root stack, but we'll return a reasonable placeholder |
| // for situations like e.g. internal logs where we just fmt %s in an |
| // arbitrary stack address that is sometimes the main stack. |
| return "<main>" |
| } |
| var buf strings.Builder |
| for i, step := range s { |
| if i != 0 { |
| buf.WriteByte('.') |
| } |
| buf.WriteString("stack.") |
| buf.WriteString(step.Name) |
| if step.Key != nil { |
| buf.WriteString(step.Key.String()) |
| } |
| } |
| return buf.String() |
| } |
| |
| func (s StackInstance) UniqueKey() collections.UniqueKey[StackInstance] { |
| return stackInstanceUniqueKey(s.String()) |
| } |
| |
| // Contains returns true if the receiver contains the given stack, or false |
| // otherwise. Contains is true if stack is a child stack of the receiver. If |
| // stack is the same as the receiver, Contains returns true. |
| func (s StackInstance) Contains(stack StackInstance) bool { |
| if len(s) > len(stack) { |
| return false |
| } |
| |
| for ix, step := range s { |
| if stack[ix].Name != step.Name { |
| return false |
| } |
| if stack[ix].Key != step.Key { |
| return false |
| } |
| } |
| return true |
| } |
| |
| type stackInstanceUniqueKey string |
| |
| // IsUniqueKey implements collections.UniqueKey. |
| func (stackInstanceUniqueKey) IsUniqueKey(StackInstance) {} |