core: ResourceAddress.Less for sorting resource addresses
Lexicographic sorting by the string form produces the wrong result because [9] sorts after [10], so this custom comparison function takes that into account and compares each portion separately to get a more intuitive result.
This commit is contained in:
parent
2051b286e0
commit
482c1f1ea5
|
@ -329,6 +329,58 @@ func (addr *ResourceAddress) Equals(raw interface{}) bool {
|
|||
modeMatch
|
||||
}
|
||||
|
||||
// Less returns true if and only if the receiver should be sorted before
|
||||
// the given address when presenting a list of resource addresses to
|
||||
// an end-user.
|
||||
//
|
||||
// This sort uses lexicographic sorting for most components, but uses
|
||||
// numeric sort for indices, thus causing index 10 to sort after
|
||||
// index 9, rather than after index 1.
|
||||
func (addr *ResourceAddress) Less(other *ResourceAddress) bool {
|
||||
|
||||
switch {
|
||||
|
||||
case len(addr.Path) < len(other.Path):
|
||||
return true
|
||||
|
||||
case !reflect.DeepEqual(addr.Path, other.Path):
|
||||
// If the two paths are the same length but don't match, we'll just
|
||||
// cheat and compare the string forms since it's easier than
|
||||
// comparing all of the path segments in turn.
|
||||
addrStr := addr.String()
|
||||
otherStr := other.String()
|
||||
return addrStr < otherStr
|
||||
|
||||
case addr.Mode == config.DataResourceMode && other.Mode != config.DataResourceMode:
|
||||
return true
|
||||
|
||||
case addr.Type < other.Type:
|
||||
return true
|
||||
|
||||
case addr.Name < other.Name:
|
||||
return true
|
||||
|
||||
case addr.Index < other.Index:
|
||||
// Since "Index" is -1 for an un-indexed address, this also conveniently
|
||||
// sorts unindexed addresses before indexed ones, should they both
|
||||
// appear for some reason.
|
||||
return true
|
||||
|
||||
case other.InstanceTypeSet && !addr.InstanceTypeSet:
|
||||
return true
|
||||
|
||||
case addr.InstanceType < other.InstanceType:
|
||||
// InstanceType is actually an enum, so this is just an arbitrary
|
||||
// sort based on the enum numeric values, and thus not particularly
|
||||
// meaningful.
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func ParseResourceIndex(s string) (int, error) {
|
||||
if s == "" {
|
||||
return -1, nil
|
||||
|
|
|
@ -1221,3 +1221,88 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceAddressLess(t *testing.T) {
|
||||
tests := []struct {
|
||||
A string
|
||||
B string
|
||||
Want bool
|
||||
}{
|
||||
{
|
||||
"foo.bar",
|
||||
"module.baz.foo.bar",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"module.baz.foo.bar",
|
||||
"module.baz.foo.bar",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"module.baz.foo.bar",
|
||||
"module.boz.foo.bar",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a.b",
|
||||
"b.c",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a.b",
|
||||
"a.c",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a.b[9]",
|
||||
"a.b[10]",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a.b",
|
||||
"a.b.deposed",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a.b.tainted",
|
||||
"a.b.deposed",
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s < %s", test.A, test.B), func(t *testing.T) {
|
||||
addrA, err := ParseResourceAddress(test.A)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
addrB, err := ParseResourceAddress(test.B)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := addrA.Less(addrB)
|
||||
invGot := addrB.Less(addrA)
|
||||
if got != test.Want {
|
||||
t.Errorf(
|
||||
"wrong result\ntest: %s < %s\ngot: %#v\nwant: %#v",
|
||||
test.A, test.B, got, test.Want,
|
||||
)
|
||||
}
|
||||
if test.A != test.B { // inverse test doesn't apply when equal
|
||||
if invGot != !test.Want {
|
||||
t.Errorf(
|
||||
"wrong inverse result\ntest: %s < %s\ngot: %#v\nwant: %#v",
|
||||
test.B, test.A, invGot, !test.Want,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if invGot != test.Want {
|
||||
t.Errorf(
|
||||
"wrong inverse result\ntest: %s < %s\ngot: %#v\nwant: %#v",
|
||||
test.B, test.A, invGot, test.Want,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue