2023-09-27 19:59:27 -04:00
|
|
|
// Copyright IBM Corp. 2014, 2026
|
|
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
|
|
2023-05-22 20:07:00 -04:00
|
|
|
package stackaddrs
|
|
|
|
|
|
2023-12-08 21:27:47 -05:00
|
|
|
import (
|
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/internal/collections"
|
|
|
|
|
)
|
|
|
|
|
|
2023-05-22 20:07:00 -04:00
|
|
|
// Referenceable is a type set containing all address types that can be
|
|
|
|
|
// the target of an expression-based reference within a particular stack.
|
|
|
|
|
type Referenceable interface {
|
|
|
|
|
referenceableSigil()
|
2023-05-26 15:03:47 -04:00
|
|
|
String() string
|
2023-05-22 20:07:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _ Referenceable = Component{}
|
|
|
|
|
var _ Referenceable = StackCall{}
|
|
|
|
|
var _ Referenceable = InputVariable{}
|
|
|
|
|
var _ Referenceable = LocalValue{}
|
|
|
|
|
var _ Referenceable = ProviderConfigRef{}
|
stackeval: Test-only globals
Because many stacks language features are dependent on others to do useful
work, it's tempting to focus only on integration testing of combinations
of features used together. However, we known from our experience with the
modules runtime (the "terraform" package) that over time this becomes a
huge maintenance burden, because any non-trivial change tends to
invalidate hundreds or thousands of integration tests, and because of their
broad scope its often hard in retrospect to figure out what exactly a
particular test was aiming to test vs. what it was just relying on as a
side-effect.
To try to minimize these cross-dependencies and thus enable something
closer to unit testing, here we introduce a special kind of symbol to the
stacks language which is available only to unit tests in this package.
"Test-only globals" -- an intentionally-clunky name to avoid squatting on
useful names -- can be set as part of the Main object and, when defined,
are available for use in all situations where we perform expression
evaluation against a stack. The "globals" in the name represents that,
unlike just about everything else, they are defined once but available in
all stacks in the configuration tree.
This design is a tradeoff: it introduces a bunch of extra code that is
here entirely to support testing, but hopefully this code is segregated
enough from everything else that it's unlikely to change significantly
under future maintenance, thereby hopefully minimizing the need for future
cross-cutting test maintenance too.
2023-11-06 13:28:39 -05:00
|
|
|
var _ Referenceable = TestOnlyGlobal{}
|
2023-11-07 20:56:26 -05:00
|
|
|
var _ Referenceable = ContextualRef(0)
|
|
|
|
|
|
2023-12-08 21:27:47 -05:00
|
|
|
// ReferenceableUniqueKey returns a unique key for a dynamically-typed
|
|
|
|
|
// referenceable address.
|
|
|
|
|
//
|
|
|
|
|
// Since the type of the target isn't statically known, the resulting unique
|
|
|
|
|
// key is not comparable with unique keys of the specific static types
|
|
|
|
|
// that implement [Referenceable].
|
|
|
|
|
func ReferenceableUniqueKey(addr Referenceable) collections.UniqueKey[Referenceable] {
|
|
|
|
|
// NOTE: This assumes that all distinct referenceable addresses have
|
|
|
|
|
// distinct and unique string representations.
|
|
|
|
|
return referenceableUniqueKey{
|
|
|
|
|
ty: reflect.TypeOf(addr),
|
|
|
|
|
str: addr.String(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type referenceableUniqueKey struct {
|
|
|
|
|
ty reflect.Type
|
|
|
|
|
str string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsUniqueKey implements collections.UniqueKey.
|
|
|
|
|
func (referenceableUniqueKey) IsUniqueKey(Referenceable) {}
|
|
|
|
|
|
2023-11-07 20:56:26 -05:00
|
|
|
type ContextualRef rune
|
|
|
|
|
|
|
|
|
|
const EachValue = ContextualRef('v')
|
|
|
|
|
const EachKey = ContextualRef('k')
|
|
|
|
|
const CountIndex = ContextualRef('i')
|
|
|
|
|
const Self = ContextualRef('s')
|
2024-06-14 20:31:36 -04:00
|
|
|
const TerraformApplying = ContextualRef('a')
|
2023-11-07 20:56:26 -05:00
|
|
|
|
|
|
|
|
// String implements Referenceable.
|
|
|
|
|
func (e ContextualRef) String() string {
|
|
|
|
|
switch e {
|
|
|
|
|
case EachKey:
|
|
|
|
|
return "each.key"
|
|
|
|
|
case EachValue:
|
|
|
|
|
return "each.value"
|
|
|
|
|
case CountIndex:
|
|
|
|
|
return "count.index"
|
|
|
|
|
case Self:
|
|
|
|
|
return "self"
|
2024-06-14 20:31:36 -04:00
|
|
|
case TerraformApplying:
|
|
|
|
|
return "terraform.applying"
|
2023-11-07 20:56:26 -05:00
|
|
|
default:
|
|
|
|
|
// The four constants in this package are the only valid values of this type
|
|
|
|
|
panic("invalid ContextualRef instance")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// referenceableSigil implements Referenceable.
|
|
|
|
|
func (e ContextualRef) referenceableSigil() {}
|
2023-12-08 21:27:47 -05:00
|
|
|
|
|
|
|
|
// AbsReferenceable is a [Referenceable] combined with the stack it would
|
|
|
|
|
// be resolved in.
|
|
|
|
|
//
|
|
|
|
|
// This type can be used only for [Referenceable] types that have stack-wide
|
|
|
|
|
// scope. It's not appropriate for referenceable objects with more specific
|
|
|
|
|
// scope, such as [ContextualRef], since describing those would require
|
|
|
|
|
// information about which specific block they are to be resolved within.
|
|
|
|
|
type AbsReferenceable struct {
|
|
|
|
|
Stack StackInstance
|
|
|
|
|
Item Referenceable
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r AbsReferenceable) UniqueKey() collections.UniqueKey[AbsReferenceable] {
|
|
|
|
|
return absReferenceableKey{
|
|
|
|
|
stackKey: r.Stack.UniqueKey(),
|
|
|
|
|
itemKey: ReferenceableUniqueKey(r.Item),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type absReferenceableKey struct {
|
|
|
|
|
stackKey collections.UniqueKey[StackInstance]
|
|
|
|
|
itemKey collections.UniqueKey[Referenceable]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsUniqueKey implements collections.UniqueKey.
|
|
|
|
|
func (absReferenceableKey) IsUniqueKey(AbsReferenceable) {
|
|
|
|
|
panic("unimplemented")
|
|
|
|
|
}
|