Skip to content

Commit

Permalink
feat: add support to conditional code generation on genhcl/genfile (#371
Browse files Browse the repository at this point in the history
)
  • Loading branch information
katcipis committed Jun 2, 2022
1 parent eb1cdeb commit 4e7f4a5
Show file tree
Hide file tree
Showing 7 changed files with 674 additions and 91 deletions.
4 changes: 3 additions & 1 deletion docs/codegen/generate-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Conditional code generation is achieved by the use of the `condition` attribute.
The `condition` attribute should always evaluate to a boolean. The file will
be generated only if it evaluates to **true**.

If the `condition` attribute is absent then it is assumed to be always true.
If the `condition` attribute is absent then it is assumed to be true.

Any expression that produces a boolean can be used, including references
to globals and function calls. For example:
Expand All @@ -87,3 +87,5 @@ generate_file "file" {

Will only generate the file for stacks that the expression
`tm_length(global.list) > 0` evaluates to true.

When `condition` is false the `content` attribute won't be evaluated.
4 changes: 3 additions & 1 deletion docs/codegen/generate-hcl.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ Conditional code generation is achieved by the use of the `condition` attribute.
The `condition` attribute should always evaluate to a boolean. The file will
be generated only if it evaluates to **true**.

If the `condition` attribute is absent then it is assumed to be always true.
If the `condition` attribute is absent then it is assumed to be true.

Any expression that produces a boolean can be used, including references
to globals and function calls. For example:
Expand All @@ -150,6 +150,8 @@ generate_hcl "file" {
Will only generate the file for stacks that the expression
`tm_length(global.list) > 0` evaluates to true.

When `condition` is false the `content` block won't be evaluated.


## Partial Evaluation

Expand Down
55 changes: 49 additions & 6 deletions generate/genfile/genfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,20 @@ import (
)

const (
// ErrInvalidContentType indicates the content attribute on
// generate_file has a invalid type.
// ErrInvalidContentType indicates the content attribute
// has an invalid type.
ErrInvalidContentType errors.Kind = "invalid content type"

// ErrInvalidConditionType indicates the condition attribute
// has an invalid type.
ErrInvalidConditionType errors.Kind = "invalid condition type"

// ErrContentEval indicates an error when evaluating the content attribute.
ErrContentEval errors.Kind = "evaluating content"

// ErrConditionEval indicates an error when evaluating the condition attribute.
ErrConditionEval errors.Kind = "evaluating condition"

// ErrLabelConflict indicates the two generate_file blocks
// have the same label.
ErrLabelConflict errors.Kind = "label conflict detected"
Expand All @@ -47,8 +54,9 @@ type StackFiles struct {

// File represents generated file from a single generate_file block.
type File struct {
origin string
body string
origin string
body string
condition bool
}

// Body returns the file body.
Expand All @@ -62,6 +70,12 @@ func (f File) Origin() string {
return f.origin
}

// Condition returns the result of the evaluation of the
// condition attribute for the generated code.
func (f File) Condition() bool {
return f.condition
}

// Header returns the header of this file.
func (f File) Header() string {
// For now we don't support headers for arbitrary files
Expand Down Expand Up @@ -120,6 +134,34 @@ func Load(rootdir string, sm stack.Metadata, globals stack.Globals) (StackFiles,
Str("block", name).
Logger()

logger.Trace().Msg("evaluating condition")

condition := true
if genFileBlock.block.Condition != nil {
logger.Trace().Msg("has condition attribute, evaluating it")
value, err := evalctx.Eval(genFileBlock.block.Condition.Expr)
if err != nil {
return StackFiles{}, errors.E(ErrConditionEval, err)
}
if value.Type() != cty.Bool {
return StackFiles{}, errors.E(
ErrInvalidConditionType,
"condition has type %s but must be boolean",
value.Type().FriendlyName(),
)
}
condition = value.True()
}

if !condition {
logger.Trace().Msg("condition=false, content wont be evaluated")
res.files[name] = File{
origin: genFileBlock.origin,
condition: condition,
}
continue
}

logger.Trace().Msg("evaluating contents")

value, err := evalctx.Eval(genFileBlock.block.Content.Expr)
Expand All @@ -136,8 +178,9 @@ func Load(rootdir string, sm stack.Metadata, globals stack.Globals) (StackFiles,
}

res.files[name] = File{
origin: genFileBlock.origin,
body: value.AsString(),
origin: genFileBlock.origin,
body: value.AsString(),
condition: condition,
}
}

Expand Down

0 comments on commit 4e7f4a5

Please sign in to comment.