hashicorp / hcl2

Former temporary home for experimental new version of HCL
https://github.com/hashicorp/hcl
Mozilla Public License 2.0
375 stars 64 forks source link

Question: how to decode a custom type and validate it? #92

Closed schu closed 5 years ago

schu commented 5 years ago

Hey, given the following example code:

package main

import (
        "fmt"

        "github.com/hashicorp/hcl2/gohcl"
        "github.com/hashicorp/hcl2/hclparse"
)

const configSrc = `
config {
        type = "foo"
}
`

type MyType string

const (
        Foo MyType = "foo"
        Bar        = "bar"
)

type Config struct {
        Type string `hcl:"type,attr"`
}

type Root struct {
        Config Config `hcl:"config,block"`
}

func main() {
        parser := hclparse.NewParser()

        file, diags := parser.ParseHCL([]byte(configSrc), "demo.hcl")
        if len(diags) != 0 {
                for _, diag := range diags {
                        fmt.Printf("%+v\n", diag)
                }
                return
        }

        configBody := file.Body

        var root Root

        diags = gohcl.DecodeBody(configBody, nil, &root)
        if len(diags) != 0 {
                for _, diag := range diags {
                        fmt.Printf("%+v\n", diag)
                }
                return
        }

        fmt.Printf("%+v\n", root)
}

If only Foo or Bar should be accepted values for Config.Type, is there a way to enforce that through hcl means (e.g. hcldec.Decode with an AttrSpec?) or would I need to validate myself after decoding into string?

apparentlymart commented 5 years ago

Hi @schu!

Indeed HCL does not provide a general solution for all possible validations; HCL's own functionality is primarily concerned with checking the "shape" of the configuration: correct usage of block structure, correct argument names, etc. Aside from the simple type checking implied by the conversion to Go types, validation of values is the application's responsibility.

One tricky edge right now is that decoding into Go types necessarily loses the source location information (because there's nowhere to store it in the resulting Go values), which makes it a little tricky to report errors with particular attributes in a helpful way. I expect we'll add some new functionality to gohcl for that in future, but for now a workaround is to use hcldec.SourceRange with an AttrSpec to find a reasonable location for that attribute value when returning an error:

rng := hcldec.SourceRange(configBody, &hcldec.Spec{Name: "type", Type: cty.String})
schu commented 5 years ago

Thanks for conforming and the detailed response!