summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorMartin Atkins <mart@degeneration.co.uk>2018-02-04 09:59:20 -0800
committerMartin Atkins <mart@degeneration.co.uk>2018-02-04 09:59:20 -0800
commit1ba92ee170ac6300d0538850241a953118bc85ef (patch)
tree4fc21697f1f3124f59feb5238e2a9ee60e6864ac /cmd
parentf65a097d171ff4f5007de72041835ac192295429 (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.md25
-rw-r--r--cmd/hcldec/spec.go48
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