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  }