github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/addrs/module.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package addrs 5 6 import ( 7 "strings" 8 ) 9 10 // Module is an address for a module call within configuration. This is 11 // the static counterpart of ModuleInstance, representing a traversal through 12 // the static module call tree in configuration and does not take into account 13 // the potentially-multiple instances of a module that might be created by 14 // "count" and "for_each" arguments within those calls. 15 // 16 // This type should be used only in very specialized cases when working with 17 // the static module call tree. Type ModuleInstance is appropriate in more cases. 18 // 19 // Although Module is a slice, it should be treated as immutable after creation. 20 type Module []string 21 22 // RootModule is the module address representing the root of the static module 23 // call tree, which is also the zero value of Module. 24 // 25 // Note that this is not the root of the dynamic module tree, which is instead 26 // represented by RootModuleInstance. 27 var RootModule Module 28 29 // IsRoot returns true if the receiver is the address of the root module, 30 // or false otherwise. 31 func (m Module) IsRoot() bool { 32 return len(m) == 0 33 } 34 35 func (m Module) String() string { 36 if len(m) == 0 { 37 return "" 38 } 39 // Calculate necessary space. 40 l := 0 41 for _, step := range m { 42 l += len(step) 43 } 44 buf := strings.Builder{} 45 // 8 is len(".module.") which separates entries. 46 buf.Grow(l + len(m)*8) 47 sep := "" 48 for _, step := range m { 49 buf.WriteString(sep) 50 buf.WriteString("module.") 51 buf.WriteString(step) 52 sep = "." 53 } 54 return buf.String() 55 } 56 57 func (m Module) Equal(other Module) bool { 58 if len(m) != len(other) { 59 return false 60 } 61 for i := range m { 62 if m[i] != other[i] { 63 return false 64 } 65 } 66 return true 67 } 68 69 func (m Module) targetableSigil() { 70 // Module is targetable 71 } 72 73 // TargetContains implements Targetable for Module by returning true if the given other 74 // address either matches the receiver, is a sub-module-instance of the 75 // receiver, or is a targetable absolute address within a module that 76 // is contained within the receiver. 77 func (m Module) TargetContains(other Targetable) bool { 78 switch to := other.(type) { 79 80 case Module: 81 if len(to) < len(m) { 82 // Can't be contained if the path is shorter 83 return false 84 } 85 // Other is contained if its steps match for the length of our own path. 86 for i, ourStep := range m { 87 otherStep := to[i] 88 if ourStep != otherStep { 89 return false 90 } 91 } 92 // If we fall out here then the prefixed matched, so it's contained. 93 return true 94 95 case ModuleInstance: 96 return m.TargetContains(to.Module()) 97 98 case ConfigResource: 99 return m.TargetContains(to.Module) 100 101 case AbsResource: 102 return m.TargetContains(to.Module) 103 104 case AbsResourceInstance: 105 return m.TargetContains(to.Module) 106 107 default: 108 return false 109 } 110 } 111 112 func (m Module) AddrType() TargetableAddrType { 113 return ModuleAddrType 114 } 115 116 // Child returns the address of a child call in the receiver, identified by the 117 // given name. 118 func (m Module) Child(name string) Module { 119 ret := make(Module, 0, len(m)+1) 120 ret = append(ret, m...) 121 return append(ret, name) 122 } 123 124 // Parent returns the address of the parent module of the receiver, or the 125 // receiver itself if there is no parent (if it's the root module address). 126 func (m Module) Parent() Module { 127 if len(m) == 0 { 128 return m 129 } 130 return m[:len(m)-1] 131 } 132 133 // Call returns the module call address that corresponds to the given module 134 // instance, along with the address of the module that contains it. 135 // 136 // There is no call for the root module, so this method will panic if called 137 // on the root module address. 138 // 139 // In practice, this just turns the last element of the receiver into a 140 // ModuleCall and then returns a slice of the receiever that excludes that 141 // last part. This is just a convenience for situations where a call address 142 // is required, such as when dealing with *Reference and Referencable values. 143 func (m Module) Call() (Module, ModuleCall) { 144 if len(m) == 0 { 145 panic("cannot produce ModuleCall for root module") 146 } 147 148 caller, callName := m[:len(m)-1], m[len(m)-1] 149 return caller, ModuleCall{ 150 Name: callName, 151 } 152 } 153 154 // Ancestors returns a slice containing the receiver and all of its ancestor 155 // modules, all the way up to (and including) the root module. The result is 156 // ordered by depth, with the root module always first. 157 // 158 // Since the result always includes the root module, a caller may choose to 159 // ignore it by slicing the result with [1:]. 160 func (m Module) Ancestors() []Module { 161 ret := make([]Module, 0, len(m)+1) 162 for i := 0; i <= len(m); i++ { 163 ret = append(ret, m[:i]) 164 } 165 return ret 166 } 167 168 func (m Module) configMoveableSigil() { 169 // ModuleInstance is moveable 170 }