CanChainFrom and NestedWithin

Add implementations of CanChainFrom and NestedWithin for
MoveEndpointInModule.

CanChainFrom allows the linking of move statements of the same address,
which means the prior destination address must equal the following
source address. If the destination and source addresses are of different
types, they must be covered by NestedWithin rather than CanChainFrom.

NestedWithin checks if the destination contains the source address. Any
matching types would be covered by CanChainFrom.
This commit is contained in:
James Bardin 2021-08-05 15:56:31 -04:00
parent 493ec4e6c5
commit 6087b1bdb9
2 changed files with 251 additions and 2 deletions

View File

@ -218,7 +218,35 @@ func (e *MoveEndpointInModule) SelectsModule(addr ModuleInstance) bool {
// the reciever is the "to" from one statement and the other given address
// is the "from" of another statement.
func (e *MoveEndpointInModule) CanChainFrom(other *MoveEndpointInModule) bool {
// TODO: implement
eSub := e.relSubject
oSub := other.relSubject
switch oSub := oSub.(type) {
case AbsModuleCall:
switch eSub := eSub.(type) {
case AbsModuleCall:
return eSub.Equal(oSub)
}
case ModuleInstance:
switch eSub := eSub.(type) {
case ModuleInstance:
return eSub.Equal(oSub)
}
case AbsResource:
switch eSub := eSub.(type) {
case AbsResource:
return eSub.Equal(oSub)
}
case AbsResourceInstance:
switch eSub := eSub.(type) {
case AbsResourceInstance:
return eSub.Equal(oSub)
}
}
return false
}
@ -226,7 +254,52 @@ func (e *MoveEndpointInModule) CanChainFrom(other *MoveEndpointInModule) bool {
// contained within one of the objects that the given other address could
// select.
func (e *MoveEndpointInModule) NestedWithin(other *MoveEndpointInModule) bool {
// TODO: implement
eSub := e.relSubject
oSub := other.relSubject
switch oSub := oSub.(type) {
case AbsModuleCall:
withinModuleCall := func(mod ModuleInstance, call AbsModuleCall) bool {
// parent modules don't match at all
if !call.Module.IsAncestor(mod) {
return false
}
rem := mod[len(call.Module):]
return rem[0].Name == call.Call.Name
}
// Module calls can contain module instances, resources, and resource
// instances.
switch eSub := eSub.(type) {
case AbsResource:
return withinModuleCall(eSub.Module, oSub)
case AbsResourceInstance:
return withinModuleCall(eSub.Module, oSub)
case ModuleInstance:
return withinModuleCall(eSub, oSub)
}
case ModuleInstance:
// Module instances can contain resources and resource instances.
switch eSub := eSub.(type) {
case AbsResource:
return eSub.Module.Equal(oSub) || oSub.IsAncestor(eSub.Module)
case AbsResourceInstance:
return eSub.Module.Equal(oSub) || oSub.IsAncestor(eSub.Module)
}
case AbsResource:
// A resource can only contain a resource instance.
switch eSub := eSub.(type) {
case AbsResourceInstance:
return eSub.ContainingResource().Equal(oSub)
}
}
return false
}

View File

@ -1074,3 +1074,179 @@ func TestAbsResourceMoveDestination(t *testing.T) {
)
}
}
func TestMoveEndpointChainAndNested(t *testing.T) {
tests := []struct {
Endpoint, Other AbsMoveable
CanChainFrom, NestedWithin bool
}{
{
Endpoint: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
Other: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
CanChainFrom: true,
NestedWithin: false,
},
{
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
Other: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
CanChainFrom: false,
NestedWithin: false,
},
{
Endpoint: mustParseModuleInstanceStr("module.foo[2].module.bar[2]"),
Other: AbsModuleCall{
Module: RootModuleInstance,
Call: ModuleCall{Name: "foo"},
},
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].module.bar.resource.baz").ContainingResource(),
Other: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].module.bar[3].resource.baz[2]"),
Other: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
Other: mustParseModuleInstanceStr("module.foo[2]"),
CanChainFrom: false,
NestedWithin: false,
},
{
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
Other: mustParseModuleInstanceStr("module.foo[2]"),
CanChainFrom: true,
NestedWithin: false,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
Other: mustParseModuleInstanceStr("module.foo[2]"),
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].module.bar.resource.baz"),
Other: mustParseModuleInstanceStr("module.foo[2]"),
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
CanChainFrom: false,
NestedWithin: false,
},
{
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
CanChainFrom: false,
NestedWithin: false,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
CanChainFrom: true,
NestedWithin: false,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz[2]").ContainingResource(),
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
CanChainFrom: false,
},
{
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
CanChainFrom: false,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
CanChainFrom: false,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
CanChainFrom: true,
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("[%02d]%s.CanChainFrom(%s)", i, test.Endpoint, test.Other),
func(t *testing.T) {
endpoint := &MoveEndpointInModule{
relSubject: test.Endpoint,
}
other := &MoveEndpointInModule{
relSubject: test.Other,
}
if endpoint.CanChainFrom(other) != test.CanChainFrom {
t.Errorf("expected %s CanChainFrom %s == %t", test.Endpoint, test.Other, test.CanChainFrom)
}
if endpoint.NestedWithin(other) != test.NestedWithin {
t.Errorf("expected %s NestedWithin %s == %t", test.Endpoint, test.Other, test.NestedWithin)
}
},
)
}
}
func mustParseAbsResourceInstanceStr(s string) AbsResourceInstance {
r, diags := ParseAbsResourceInstanceStr(s)
if diags.HasErrors() {
panic(diags.ErrWithWarnings().Error())
}
return r
}