Documentation ¶
Overview ¶
Package hclext is an extension of package hcl for TFLint.
The goal of this package is to work with nested hcl.BodyContent. In the various functions provided by the package hcl, hcl.Block nests hcl.Body as body. However, since hcl.Body is an interface, the nested body cannot be sent over a wire protocol.
In this package, redefine hcl.Block as hclext.Block nests BodyContent, not Body, which is an interface. Some functions and related structures have been redefined to make hclext.Block behave like the package hcl.
For example, Content/PartialContent takes hclext.BodySchema instead of hcl.BodySchema and returns hclext.BodyContent. In hclext.BodySchema, you can declare the structure of the nested body as the block schema. This allows you to send the schema and its results of configurations that contain nested bodies via gRPC.
Index ¶
- func BindValue(val cty.Value, expr hcl.Expression) hcl.Expression
- func DecodeBody(body *BodyContent, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics
- func ParseExpression(src []byte, filename string, start hcl.Pos) (hcl.Expression, hcl.Diagnostics)
- type Attribute
- type AttributeSchema
- type Attributes
- type Block
- type BlockSchema
- type Blocks
- type BodyContent
- type BodySchema
- type BoundExpr
- type SchemaMode
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BindValue ¶ added in v0.14.0
BindValue binds the passed value to an expression. This returns the bound expression.
func DecodeBody ¶
func DecodeBody(body *BodyContent, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics
DecodeBody is a derivative of gohcl.DecodeBody the receives hclext.BodyContent instead of hcl.Body. Since hcl.Body is hard to send over a wire protocol, it is needed to support BodyContent. This method differs from gohcl.DecodeBody in several ways:
- Does not support decoding to map, cty.Value, hcl.Body, hcl.Expression. - Does not support `body` and `remain` tags.
- Extraneous attributes are always ignored.
@see https://github.com/hashicorp/hcl/blob/v2.11.1/gohcl/decode.go
Example ¶
src := ` noodle "foo" "bar" { type = "rice" bread "baz" { type = "focaccia" baked = true } bread "quz" { type = "rye" } }` file, diags := hclsyntax.ParseConfig([]byte(src), "test.tf", hcl.InitialPos) if diags.HasErrors() { panic(diags) } type Bread struct { // The `*,label` tag matches "bread" block labels. // The count of tags should be matched to count of block labels. Name string `hclext:"name,label"` // The `type` tag matches a "type" attribute inside of "bread" block. Type string `hclext:"type"` // The `baked,optional` tag matches a "baked" attribute, but it is optional. Baked bool `hclext:"baked,optional"` } type Noodle struct { Name string `hclext:"name,label"` SubName string `hclext:"subname,label"` Type string `hclext:"type"` // The `bread,block` tag matches "bread" blocks. // Multiple blocks are allowed because the field type is slice. Breads []Bread `hclext:"bread,block"` } type Config struct { // Only 1 block must be needed because the field type is not slice, not a pointer. Noodle Noodle `hclext:"noodle,block"` } target := &Config{} schema := ImpliedBodySchema(target) body, diags := Content(file.Body, schema) if diags.HasErrors() { panic(diags) } diags = DecodeBody(body, nil, target) if diags.HasErrors() { panic(diags) } fmt.Printf("- noodle: name=%s, subname=%s type=%s\n", target.Noodle.Name, target.Noodle.SubName, target.Noodle.Type) for i, bread := range target.Noodle.Breads { fmt.Printf(" - bread[%d]: name=%s, type=%s baked=%t\n", i, bread.Name, bread.Type, bread.Baked) }
Output: - noodle: name=foo, subname=bar type=rice - bread[0]: name=baz, type=focaccia baked=true - bread[1]: name=quz, type=rye baked=false
func ParseExpression ¶
ParseExpression is a wrapper that calls ParseExpression of hclsyntax and json based on the file extension. This function specializes in parsing intermediate expressions in the file, so it takes into account the hack on trailing newlines in heredoc.
Types ¶
type Attribute ¶
type Attribute struct { Name string Expr hcl.Expression Range hcl.Range NameRange hcl.Range }
Attribute represents an attribute from within a body.
type AttributeSchema ¶
AttributeSchema represents the desired attribute. This structure is designed to have attributes similar to hcl.AttributeSchema.
type Attributes ¶
Attributes is a set of attributes keyed by their names. Please note that this is not strictly. Since hclext.BodyContent is the body from multiple files, top-level attributes can have the same name (it is not possible to specify the same name within a block). This exception is not considered here, as Terraform syntax does not allow top-level attributes.
func (Attributes) AsNative ¶ added in v0.12.0
func (as Attributes) AsNative() hcl.Attributes
AsNative returns self as hcl.Attributes
type Block ¶
type Block struct { Type string Labels []string Body *BodyContent DefRange hcl.Range TypeRange hcl.Range LabelRanges []hcl.Range }
Block represents a nested block within a hcl.Body. Unlike hcl.Block, this has Body as hclext.BodyContent (struct), not hcl.Body (interface). Since interface is hard to send over a wire protocol, it is designed to always return only the attributes based on the schema. Instead, the hclext.BlockSchema can now be nested to extract the attributes within the nested block.
type BlockSchema ¶
type BlockSchema struct { Type string LabelNames []string Body *BodySchema }
BlockSchema represents the desired block header and body schema. Unlike hcl.BlockHeaderSchema, this can set nested body schema. Instead, hclext.Block can't handle abstract values like hcl.Body, so you need to specify all nested schemas at once.
type Blocks ¶
type Blocks []*Block
Blocks is a sequence of Block.
type BodyContent ¶
type BodyContent struct { Attributes Attributes Blocks Blocks }
BodyContent is the result of applying a hclext.BodySchema to a hcl.Body. Unlike hcl.BodyContent, this does not have MissingItemRange. This difference is because hcl.BodyContent is the result for a single HCL file, while hclext.BodyContent is the result for a Terraform module.
func Content ¶
func Content(body hcl.Body, schema *BodySchema) (*BodyContent, hcl.Diagnostics)
Content is a wrapper for hcl.Content for working with nested schemas. Convert hclext.BodySchema to hcl.BodySchema, and convert hcl.BodyContent to hclext.BodyContent. It processes the nested body recursively.
Example ¶
src := ` noodle "foo" "bar" { type = "rice" bread "baz" { type = "focaccia" baked = true } bread "quz" { type = "rye" } }` file, diags := hclsyntax.ParseConfig([]byte(src), "test.tf", hcl.InitialPos) if diags.HasErrors() { panic(diags) } body, diags := Content(file.Body, &BodySchema{ Blocks: []BlockSchema{ { Type: "noodle", LabelNames: []string{"name", "subname"}, Body: &BodySchema{ Attributes: []AttributeSchema{{Name: "type"}}, Blocks: []BlockSchema{ { Type: "bread", LabelNames: []string{"name"}, Body: &BodySchema{ Attributes: []AttributeSchema{ {Name: "type", Required: true}, {Name: "baked"}, }, }, }, }, }, }, }, }) if diags.HasErrors() { panic(diags) } for i, noodle := range body.Blocks { fmt.Printf("- noodle[%d]: labels=%s, attributes=%d\n", i, noodle.Labels, len(noodle.Body.Attributes)) for i, bread := range noodle.Body.Blocks { fmt.Printf(" - bread[%d]: labels=%s, attributes=%d\n", i, bread.Labels, len(bread.Body.Attributes)) } }
Output: - noodle[0]: labels=[foo bar], attributes=1 - bread[0]: labels=[baz], attributes=2 - bread[1]: labels=[quz], attributes=1
func PartialContent ¶
func PartialContent(body hcl.Body, schema *BodySchema) (*BodyContent, hcl.Diagnostics)
PartialContent is a wrapper for hcl.PartialContent for working with nested schemas. Convert hclext.BodySchema to hcl.BodySchema, and convert hcl.BodyContent to hclext.BodyContent. It processes the nested body recursively. Unlike hcl.PartialContent, it does not return the rest of the body.
Example ¶
src := ` noodle "foo" "bar" { type = "rice" bread "baz" { type = "focaccia" baked = true } bread "quz" { type = "rye" } }` file, diags := hclsyntax.ParseConfig([]byte(src), "test.tf", hcl.InitialPos) if diags.HasErrors() { panic(diags) } body, diags := PartialContent(file.Body, &BodySchema{ Blocks: []BlockSchema{ { Type: "noodle", LabelNames: []string{"name", "subname"}, Body: &BodySchema{ Blocks: []BlockSchema{ { Type: "bread", LabelNames: []string{"name"}, Body: &BodySchema{ Attributes: []AttributeSchema{ {Name: "type", Required: true}, }, }, }, }, }, }, }, }) if diags.HasErrors() { panic(diags) } for i, noodle := range body.Blocks { fmt.Printf("- noodle[%d]: labels=%s, attributes=%d\n", i, noodle.Labels, len(noodle.Body.Attributes)) for i, bread := range noodle.Body.Blocks { fmt.Printf(" - bread[%d]: labels=%s, attributes=%d\n", i, bread.Labels, len(bread.Body.Attributes)) } }
Output: - noodle[0]: labels=[foo bar], attributes=0 - bread[0]: labels=[baz], attributes=1 - bread[1]: labels=[quz], attributes=1
func (*BodyContent) Copy ¶ added in v0.14.0
func (b *BodyContent) Copy() *BodyContent
Copy returns a new BodyContent based on the original.
func (*BodyContent) IsEmpty ¶ added in v0.12.0
func (b *BodyContent) IsEmpty() bool
IsEmpty returns whether the body content is empty
func (*BodyContent) WalkAttributes ¶ added in v0.14.0
func (b *BodyContent) WalkAttributes(walker func(*Attribute) hcl.Diagnostics) hcl.Diagnostics
WalkAttributes visits all attributes with the passed walker function.
type BodySchema ¶
type BodySchema struct { Mode SchemaMode Attributes []AttributeSchema Blocks []BlockSchema }
BodySchema represents the desired body. This structure is designed to have attributes similar to hcl.BodySchema.
func ImpliedBodySchema ¶
func ImpliedBodySchema(val interface{}) *BodySchema
ImpliedBodySchema is a derivative of gohcl.ImpliedBodySchema that produces hclext.BodySchema instead of hcl.BodySchema. Unlike gohcl.ImpliedBodySchema, it produces nested schemas. This method differs from gohcl.DecodeBody in several ways:
- Does not support `body` and `remain` tags. - Does not support partial schema.
@see https://github.com/hashicorp/hcl/blob/v2.11.1/gohcl/schema.go
type BoundExpr ¶ added in v0.14.0
BoundExpr represents an expression whose a value is bound. This is a wrapper for any expression, typically satisfying an interface to behave like the wrapped expression.
The difference is that when resolving a value with `Value()`, instead of resolving the variables with EvalContext, the bound value is returned directly.
func (BoundExpr) Range ¶ added in v0.14.0
func (e BoundExpr) Range() hcl.Range
Range delegates to the wrapped expression.
func (BoundExpr) StartRange ¶ added in v0.14.0
func (e BoundExpr) StartRange() hcl.Range
StartRange delegates to the wrapped expression.
func (BoundExpr) UnwrapExpression ¶ added in v0.18.0
func (e BoundExpr) UnwrapExpression() hcl.Expression
UnwrapExpression returns the original expression. This satisfies the hcl.unwrapExpression interface.
type SchemaMode ¶ added in v0.14.0
type SchemaMode int32
SchemaMode controls how the body's schema is declared.
const ( // SchemaDefaultMode is a mode for explicitly declaring the structure of attributes and blocks. SchemaDefaultMode SchemaMode = iota // SchemaJustAttributesMode is the mode to extract body as attributes. // In this mode you don't need to declare schema for attributes or blocks. SchemaJustAttributesMode )
func (SchemaMode) String ¶ added in v0.14.0
func (i SchemaMode) String() string