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 }