// 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 "
" } 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 "
" } 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) {}