github.com/opentofu/opentofu@v1.7.1/internal/legacy/helper/schema/field_reader_multi.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 schema
     7  
     8  import (
     9  	"fmt"
    10  )
    11  
    12  // MultiLevelFieldReader reads from other field readers,
    13  // merging their results along the way in a specific order. You can specify
    14  // "levels" and name them in order to read only an exact level or up to
    15  // a specific level.
    16  //
    17  // This is useful for saying things such as "read the field from the state
    18  // and config and merge them" or "read the latest value of the field".
    19  type MultiLevelFieldReader struct {
    20  	Readers map[string]FieldReader
    21  	Levels  []string
    22  }
    23  
    24  func (r *MultiLevelFieldReader) ReadField(address []string) (FieldReadResult, error) {
    25  	return r.ReadFieldMerge(address, r.Levels[len(r.Levels)-1])
    26  }
    27  
    28  func (r *MultiLevelFieldReader) ReadFieldExact(
    29  	address []string, level string) (FieldReadResult, error) {
    30  	reader, ok := r.Readers[level]
    31  	if !ok {
    32  		return FieldReadResult{}, fmt.Errorf(
    33  			"Unknown reader level: %s", level)
    34  	}
    35  
    36  	result, err := reader.ReadField(address)
    37  	if err != nil {
    38  		return FieldReadResult{}, fmt.Errorf(
    39  			"Error reading level %s: %w", level, err)
    40  	}
    41  
    42  	return result, nil
    43  }
    44  
    45  func (r *MultiLevelFieldReader) ReadFieldMerge(
    46  	address []string, level string) (FieldReadResult, error) {
    47  	var result FieldReadResult
    48  	for _, l := range r.Levels {
    49  		if r, ok := r.Readers[l]; ok {
    50  			out, err := r.ReadField(address)
    51  			if err != nil {
    52  				return FieldReadResult{}, fmt.Errorf(
    53  					"Error reading level %s: %w", l, err)
    54  			}
    55  
    56  			// TODO: computed
    57  			if out.Exists {
    58  				result = out
    59  			}
    60  		}
    61  
    62  		if l == level {
    63  			break
    64  		}
    65  	}
    66  
    67  	return result, nil
    68  }