| // Copyright IBM Corp. 2014, 2026 | |
| // 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()) | |
| } | |
| // ToStackCall converts the stack address into the absolute address of the stack | |
| // call that would create this stack. | |
| func (s Stack) ToStackCall() ConfigStackCall { | |
| return ConfigStackCall{ | |
| Stack: s.Parent(), | |
| Item: StackCall{ | |
| Name: s[len(s)-1].Name, | |
| }, | |
| } | |
| } | |
| 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) {} |