github.com/opentofu/opentofu@v1.7.1/internal/configs/configschema/implied_type.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package configschema
     7  
     8  import (
     9  	"github.com/hashicorp/hcl/v2/hcldec"
    10  	"github.com/zclconf/go-cty/cty"
    11  )
    12  
    13  // ImpliedType returns the cty.Type that would result from decoding a
    14  // configuration block using the receiving block schema.
    15  //
    16  // The type returned from Block.ImpliedType differs from the type returned by
    17  // hcldec.ImpliedType in that there will be no objects with optional
    18  // attributes, since this value is not to be used for the decoding of
    19  // configuration.
    20  //
    21  // ImpliedType always returns a result, even if the given schema is
    22  // inconsistent. Code that creates configschema.Block objects should be
    23  // tested using the InternalValidate method to detect any inconsistencies
    24  // that would cause this method to fall back on defaults and assumptions.
    25  func (b *Block) ImpliedType() cty.Type {
    26  	return b.specType().WithoutOptionalAttributesDeep()
    27  }
    28  
    29  // specType returns the cty.Type used for decoding a configuration
    30  // block using the receiving block schema. This is the type used internally by
    31  // hcldec to decode configuration.
    32  func (b *Block) specType() cty.Type {
    33  	if b == nil {
    34  		return cty.EmptyObject
    35  	}
    36  
    37  	return hcldec.ImpliedType(b.DecoderSpec())
    38  }
    39  
    40  // ContainsSensitive returns true if any of the attributes of the receiving
    41  // block or any of its descendent blocks are marked as sensitive.
    42  //
    43  // Blocks themselves cannot be sensitive as a whole -- sensitivity is a
    44  // per-attribute idea -- but sometimes we want to include a whole object
    45  // decoded from a block in some UI output, and that is safe to do only if
    46  // none of the contained attributes are sensitive.
    47  func (b *Block) ContainsSensitive() bool {
    48  	for _, attrS := range b.Attributes {
    49  		if attrS.Sensitive {
    50  			return true
    51  		}
    52  		if attrS.NestedType != nil && attrS.NestedType.ContainsSensitive() {
    53  			return true
    54  		}
    55  	}
    56  	for _, blockS := range b.BlockTypes {
    57  		if blockS.ContainsSensitive() {
    58  			return true
    59  		}
    60  	}
    61  	return false
    62  }
    63  
    64  // ImpliedType returns the cty.Type that would result from decoding a Block's
    65  // ImpliedType and getting the resulting AttributeType.
    66  //
    67  // ImpliedType always returns a result, even if the given schema is
    68  // inconsistent. Code that creates configschema.Object objects should be tested
    69  // using the InternalValidate method to detect any inconsistencies that would
    70  // cause this method to fall back on defaults and assumptions.
    71  func (a *Attribute) ImpliedType() cty.Type {
    72  	if a.NestedType != nil {
    73  		return a.NestedType.specType().WithoutOptionalAttributesDeep()
    74  	}
    75  	return a.Type
    76  }
    77  
    78  // ImpliedType returns the cty.Type that would result from decoding a
    79  // NestedType Attribute using the receiving block schema.
    80  //
    81  // ImpliedType always returns a result, even if the given schema is
    82  // inconsistent. Code that creates configschema.Object objects should be tested
    83  // using the InternalValidate method to detect any inconsistencies that would
    84  // cause this method to fall back on defaults and assumptions.
    85  func (o *Object) ImpliedType() cty.Type {
    86  	return o.specType().WithoutOptionalAttributesDeep()
    87  }
    88  
    89  // specType returns the cty.Type used for decoding a NestedType Attribute using
    90  // the receiving block schema.
    91  func (o *Object) specType() cty.Type {
    92  	if o == nil {
    93  		return cty.EmptyObject
    94  	}
    95  
    96  	attrTys := make(map[string]cty.Type, len(o.Attributes))
    97  	for name, attrS := range o.Attributes {
    98  		if attrS.NestedType != nil {
    99  			attrTys[name] = attrS.NestedType.specType()
   100  		} else {
   101  			attrTys[name] = attrS.Type
   102  		}
   103  	}
   104  	optAttrs := listOptionalAttrsFromObject(o)
   105  
   106  	var ret cty.Type
   107  	if len(optAttrs) > 0 {
   108  		ret = cty.ObjectWithOptionalAttrs(attrTys, optAttrs)
   109  	} else {
   110  		ret = cty.Object(attrTys)
   111  	}
   112  	switch o.Nesting {
   113  	case NestingSingle:
   114  		return ret
   115  	case NestingList:
   116  		return cty.List(ret)
   117  	case NestingMap:
   118  		return cty.Map(ret)
   119  	case NestingSet:
   120  		return cty.Set(ret)
   121  	default: // Should never happen
   122  		return cty.EmptyObject
   123  	}
   124  }
   125  
   126  // ContainsSensitive returns true if any of the attributes of the receiving
   127  // Object are marked as sensitive.
   128  func (o *Object) ContainsSensitive() bool {
   129  	for _, attrS := range o.Attributes {
   130  		if attrS.Sensitive {
   131  			return true
   132  		}
   133  		if attrS.NestedType != nil && attrS.NestedType.ContainsSensitive() {
   134  			return true
   135  		}
   136  	}
   137  	return false
   138  }