zcldec: initial implementation of BlockListSpec
This initial pass supports only blocks without labels. BlockLabelSpec will be wired in here later to allow decoding blocks with labels too.
This commit is contained in:
parent
4df9fd1372
commit
0598a0b79b
@ -146,6 +146,58 @@ b {}
|
||||
cty.EmptyObjectVal,
|
||||
1, // only one "b" block is allowed
|
||||
},
|
||||
{
|
||||
`
|
||||
b {}
|
||||
b {}
|
||||
`,
|
||||
&BlockListSpec{
|
||||
TypeName: "b",
|
||||
Nested: ObjectSpec{},
|
||||
},
|
||||
nil,
|
||||
cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
``,
|
||||
&BlockListSpec{
|
||||
TypeName: "b",
|
||||
Nested: ObjectSpec{},
|
||||
},
|
||||
nil,
|
||||
cty.ListValEmpty(cty.DynamicPseudoType),
|
||||
0,
|
||||
},
|
||||
{
|
||||
`
|
||||
b {}
|
||||
b {}
|
||||
b {}
|
||||
`,
|
||||
&BlockListSpec{
|
||||
TypeName: "b",
|
||||
Nested: ObjectSpec{},
|
||||
MaxItems: 2,
|
||||
},
|
||||
nil,
|
||||
cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal, cty.EmptyObjectVal}),
|
||||
1, // too many b blocks
|
||||
},
|
||||
{
|
||||
`
|
||||
b {}
|
||||
b {}
|
||||
`,
|
||||
&BlockListSpec{
|
||||
TypeName: "b",
|
||||
Nested: ObjectSpec{},
|
||||
MinItems: 10,
|
||||
},
|
||||
nil,
|
||||
cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}),
|
||||
1, // insufficient b blocks
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
|
@ -360,8 +360,80 @@ func (s *BlockListSpec) visitSameBodyChildren(cb visitFunc) {
|
||||
// leaf node ("Nested" does not use the same body)
|
||||
}
|
||||
|
||||
// blockSpec implementation
|
||||
func (s *BlockListSpec) blockHeaderSchemata() []zcl.BlockHeaderSchema {
|
||||
return []zcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: s.TypeName,
|
||||
// FIXME: Need to peek into s.Nested to see if it has any
|
||||
// BlockLabelSpec instances, which will define then how many
|
||||
// labels we need.
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// specNeedingVariables implementation
|
||||
func (s *BlockListSpec) variablesNeeded(content *zcl.BodyContent) []zcl.Traversal {
|
||||
var ret []zcl.Traversal
|
||||
|
||||
for _, childBlock := range content.Blocks {
|
||||
if childBlock.Type != s.TypeName {
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, Variables(childBlock.Body, s.Nested)...)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *BlockListSpec) decode(content *zcl.BodyContent, block *zcl.Block, ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
|
||||
panic("BlockListSpec.decode not yet implemented")
|
||||
var diags zcl.Diagnostics
|
||||
|
||||
if s.Nested == nil {
|
||||
panic("BlockSpec with no Nested Spec")
|
||||
}
|
||||
|
||||
var elems []cty.Value
|
||||
var sourceRanges []zcl.Range
|
||||
for _, childBlock := range content.Blocks {
|
||||
if childBlock.Type != s.TypeName {
|
||||
continue
|
||||
}
|
||||
|
||||
val, _, childDiags := decode(childBlock.Body, childBlock, ctx, s.Nested, false)
|
||||
diags = append(diags, childDiags...)
|
||||
elems = append(elems, val)
|
||||
sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, childBlock, s.Nested))
|
||||
}
|
||||
|
||||
if len(elems) < s.MinItems {
|
||||
diags = append(diags, &zcl.Diagnostic{
|
||||
Severity: zcl.DiagError,
|
||||
Summary: fmt.Sprintf("insufficient %s blocks", s.TypeName),
|
||||
Detail: fmt.Sprintf("at least %d blocks are required", s.MinItems),
|
||||
Subject: &content.MissingItemRange,
|
||||
})
|
||||
} else if s.MaxItems > 0 && len(elems) > s.MaxItems {
|
||||
diags = append(diags, &zcl.Diagnostic{
|
||||
Severity: zcl.DiagError,
|
||||
Summary: fmt.Sprintf("too many %s blocks", s.TypeName),
|
||||
Detail: fmt.Sprintf("no more than %d blocks are allowed", s.MaxItems),
|
||||
Subject: &sourceRanges[s.MaxItems],
|
||||
})
|
||||
}
|
||||
|
||||
var ret cty.Value
|
||||
|
||||
if len(elems) == 0 {
|
||||
// FIXME: We don't currently have enough info to construct a type for
|
||||
// an empty list, so we'll just stub it out.
|
||||
ret = cty.ListValEmpty(cty.DynamicPseudoType)
|
||||
} else {
|
||||
ret = cty.ListVal(elems)
|
||||
}
|
||||
|
||||
return ret, diags
|
||||
}
|
||||
|
||||
func (s *BlockListSpec) sourceRange(content *zcl.BodyContent, block *zcl.Block) zcl.Range {
|
||||
|
@ -85,6 +85,45 @@ b {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
`
|
||||
b {
|
||||
a = foo
|
||||
}
|
||||
b {
|
||||
a = bar
|
||||
}
|
||||
c {
|
||||
a = baz
|
||||
}
|
||||
`,
|
||||
&BlockListSpec{
|
||||
TypeName: "b",
|
||||
Nested: &AttrSpec{
|
||||
Name: "a",
|
||||
},
|
||||
},
|
||||
[]zcl.Traversal{
|
||||
{
|
||||
zcl.TraverseRoot{
|
||||
Name: "foo",
|
||||
SrcRange: zcl.Range{
|
||||
Start: zcl.Pos{Line: 3, Column: 7, Byte: 11},
|
||||
End: zcl.Pos{Line: 3, Column: 10, Byte: 14},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
zcl.TraverseRoot{
|
||||
Name: "bar",
|
||||
SrcRange: zcl.Range{
|
||||
Start: zcl.Pos{Line: 6, Column: 7, Byte: 27},
|
||||
End: zcl.Pos{Line: 6, Column: 10, Byte: 30},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
|
Loading…
Reference in New Issue
Block a user