github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/steampipeconfig/parse/decode_options.go (about)

     1  package parse
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/hashicorp/hcl/v2"
     6  	"github.com/hashicorp/hcl/v2/gohcl"
     7  	"github.com/hashicorp/hcl/v2/hclsyntax"
     8  	"github.com/turbot/pipe-fittings/hclhelpers"
     9  	"github.com/turbot/steampipe/pkg/constants"
    10  	"github.com/turbot/steampipe/pkg/steampipeconfig/options"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  // DecodeOptions decodes an options block
    15  func DecodeOptions(block *hcl.Block, overrides ...BlockMappingOverride) (options.Options, hcl.Diagnostics) {
    16  	var diags hcl.Diagnostics
    17  	mapping := defaultOptionsBlockMapping()
    18  	for _, applyOverride := range overrides {
    19  		applyOverride(mapping)
    20  	}
    21  	optionsType := block.Labels[0]
    22  
    23  	destination, ok := mapping[optionsType]
    24  	if !ok {
    25  		diags = append(diags, &hcl.Diagnostic{
    26  			Severity: hcl.DiagError,
    27  			Summary:  fmt.Sprintf("Unexpected options type '%s'", block.Labels[0]),
    28  			Subject:  hclhelpers.BlockRangePointer(block),
    29  		})
    30  		return nil, diags
    31  	}
    32  
    33  	if timingOptions, ok := destination.(options.CanSetTiming); ok {
    34  		morediags := decodeTimingFlag(block, timingOptions)
    35  		if morediags.HasErrors() {
    36  			diags = append(diags, morediags...)
    37  			return nil, diags
    38  		}
    39  	}
    40  	diags = gohcl.DecodeBody(block.Body, nil, destination)
    41  	if diags.HasErrors() {
    42  		return nil, diags
    43  	}
    44  
    45  	return destination, nil
    46  }
    47  
    48  // for Query options block,  if timing attribute is set to "verbose", replace with true and set verbose to true
    49  func decodeTimingFlag(block *hcl.Block, timingOptions options.CanSetTiming) hcl.Diagnostics {
    50  	body := block.Body.(*hclsyntax.Body)
    51  	timingAttribute := body.Attributes["timing"]
    52  	if timingAttribute == nil {
    53  		return nil
    54  	}
    55  	// remove the attribute so subsequent decoding does not see it
    56  	delete(body.Attributes, "timing")
    57  
    58  	val, diags := timingAttribute.Expr.Value(&hcl.EvalContext{
    59  		Variables: map[string]cty.Value{
    60  			constants.ArgOn:      cty.StringVal(constants.ArgOn),
    61  			constants.ArgOff:     cty.StringVal(constants.ArgOff),
    62  			constants.ArgVerbose: cty.StringVal(constants.ArgVerbose),
    63  		},
    64  	})
    65  	if diags.HasErrors() {
    66  		return diags
    67  	}
    68  	// support legacy boolean values
    69  	if val == cty.True {
    70  		val = cty.StringVal(constants.ArgOn)
    71  	}
    72  	if val == cty.False {
    73  		val = cty.StringVal(constants.ArgOff)
    74  	}
    75  	return timingOptions.SetTiming(val.AsString(), timingAttribute.Range())
    76  
    77  }
    78  
    79  type OptionsBlockMapping = map[string]options.Options
    80  
    81  func defaultOptionsBlockMapping() OptionsBlockMapping {
    82  	mapping := OptionsBlockMapping{
    83  		options.DatabaseBlock:  &options.Database{},
    84  		options.GeneralBlock:   &options.General{},
    85  		options.QueryBlock:     &options.Query{},
    86  		options.CheckBlock:     &options.Check{},
    87  		options.DashboardBlock: &options.GlobalDashboard{},
    88  		options.PluginBlock:    &options.Plugin{},
    89  	}
    90  	return mapping
    91  }
    92  
    93  type BlockMappingOverride func(OptionsBlockMapping)
    94  
    95  // WithOverride overrides the default block mapping for a single block type
    96  func WithOverride(blockName string, destination options.Options) BlockMappingOverride {
    97  	return func(mapping OptionsBlockMapping) {
    98  		mapping[blockName] = destination
    99  	}
   100  }