diff options
| author | Martin Atkins <mart@degeneration.co.uk> | 2018-02-04 09:59:20 -0800 |
|---|---|---|
| committer | Martin Atkins <mart@degeneration.co.uk> | 2018-02-04 09:59:20 -0800 |
| commit | 1ba92ee170ac6300d0538850241a953118bc85ef (patch) | |
| tree | 4fc21697f1f3124f59feb5238e2a9ee60e6864ac /cmd | |
| parent | f65a097d171ff4f5007de72041835ac192295429 (diff) | |
cmd/hcldec: "transform" spec type
This new spec type allows evaluating an arbitrary expression on the
result of a nested spec, for situations where the a value must be
transformed in some way.
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/hcldec/spec-format.md | 25 | ||||
| -rw-r--r-- | cmd/hcldec/spec.go | 48 |
2 files changed, 73 insertions, 0 deletions
diff --git a/cmd/hcldec/spec-format.md b/cmd/hcldec/spec-format.md index 7ba709b..2126d69 100644 --- a/cmd/hcldec/spec-format.md +++ b/cmd/hcldec/spec-format.md @@ -305,6 +305,31 @@ their usual behavior but are not able to impose validation constraints on the current body since they are not evaluated unless all prior specs produce `null` as their result. +## `transform` spec blocks + +The `transform` spec type evaluates one nested spec and then evaluates a given +expression with that nested spec result to produce a final value. +It creates no validation constraints of its own, but passes on the validation +constraints from its nested block. + +```hcl +transform { + attr { + name = "size_in_mb" + type = number + } + + # Convert result to a size in bytes + result = nested * 1024 * 1024 +} +``` + +`transform` spec blocks accept the following argument: + +* `result` (required) - The expression to evaluate on the result of the nested + spec. The variable `nested` is defined when evaluating this expression, with + the result value of the nested spec. + ## Type Expressions Type expressions are used to describe the expected type of an attribute, as diff --git a/cmd/hcldec/spec.go b/cmd/hcldec/spec.go index 6081d68..fa09663 100644 --- a/cmd/hcldec/spec.go +++ b/cmd/hcldec/spec.go @@ -85,6 +85,9 @@ func decodeSpecBlock(block *hcl.Block) (hcldec.Spec, hcl.Diagnostics) { case "default": return decodeDefaultSpec(block.Body) + case "transform": + return decodeTransformSpec(block.Body) + case "literal": return decodeLiteralSpec(block.Body) @@ -439,6 +442,50 @@ func decodeDefaultSpec(body hcl.Body) (hcldec.Spec, hcl.Diagnostics) { return spec, diags } +func decodeTransformSpec(body hcl.Body) (hcldec.Spec, hcl.Diagnostics) { + type content struct { + Result hcl.Expression `hcl:"result"` + Nested hcl.Body `hcl:",remain"` + } + + var args content + diags := gohcl.DecodeBody(body, nil, &args) + if diags.HasErrors() { + return errSpec, diags + } + + spec := &hcldec.TransformExprSpec{ + Expr: args.Result, + VarName: "nested", + } + + nestedContent, nestedDiags := args.Nested.Content(specSchemaUnlabelled) + diags = append(diags, nestedDiags...) + + if len(nestedContent.Blocks) != 1 { + if nestedDiags.HasErrors() { + // If we already have errors then they probably explain + // why we have the wrong number of blocks, so we'll skip our + // additional error message added below. + return errSpec, diags + } + + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid transform spec", + Detail: "A transform spec block must have exactly one nested spec block.", + Subject: body.MissingItemRange().Ptr(), + }) + return errSpec, diags + } + + nestedSpec, nestedDiags := decodeSpecBlock(nestedContent.Blocks[0]) + diags = append(diags, nestedDiags...) + spec.Wrapped = nestedSpec + + return spec, diags +} + var errSpec = &hcldec.LiteralSpec{ Value: cty.NullVal(cty.DynamicPseudoType), } @@ -457,6 +504,7 @@ var specBlockTypes = []string{ "block_set", "default", + "transform", } var specSchemaUnlabelled *hcl.BodySchema |
