terraform/internal/stacks/stackaddrs/referenceable.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

108 lines
3 KiB
Go
Raw Permalink Normal View History

// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package stackaddrs
import (
"reflect"
"github.com/hashicorp/terraform/internal/collections"
)
// 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()
String() string
}
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{}
var _ Referenceable = ContextualRef(0)
// 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) {}
type ContextualRef rune
const EachValue = ContextualRef('v')
const EachKey = ContextualRef('k')
const CountIndex = ContextualRef('i')
const Self = ContextualRef('s')
const TerraformApplying = ContextualRef('a')
// 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"
case TerraformApplying:
return "terraform.applying"
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() {}
// 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")
}