pkg/model/history/sorter_impl.go (92 lines of code) (raw):

// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package history import ( "fmt" "log/slog" "slices" "sort" "strings" "github.com/GoogleCloudPlatform/khi/pkg/model/enum" ) type NameSortStrategy struct { PrioritizedKeys []string Layer int } // SortChunk implements ResourceChunkSortStrategy. func (n *NameSortStrategy) SortChunk(builder *Builder, parents []*Resource, groupedRelationship enum.ParentRelationship, chunk []*Resource) ([]*Resource, error) { if len(parents) != n.Layer { return nil, ErrorSortSkipped } sortResult := slices.Clone(chunk) keyToIndex := make(map[string]int) for index, key := range n.PrioritizedKeys { keyToIndex[key] = index } priority := func(key string) int { index, ok := keyToIndex[key] if ok { return index } return len(n.PrioritizedKeys) } sort.Slice(sortResult, func(i, j int) bool { a := priority(sortResult[i].ResourceName) b := priority(sortResult[j].ResourceName) reltypeA := prorityByRelationship(sortResult[i].Relationship) relTypeB := prorityByRelationship(sortResult[j].Relationship) switch { case a != b: return a < b case reltypeA != relTypeB: return reltypeA < relTypeB default: return sortResult[i].ResourceName < sortResult[j].ResourceName } }) return sortResult, nil } func prorityByRelationship(rel enum.ParentRelationship) int { return enum.ParentRelationships[rel].SortPriority } var _ ResourceChunkSortStrategy = (*NameSortStrategy)(nil) func NewNameSortStrategy(layer int, prioritizedKeys []string) *NameSortStrategy { return &NameSortStrategy{ Layer: layer, PrioritizedKeys: prioritizedKeys, } } // UnreachableSortStrategy is the default sort strategy catches all. // Hitting this sorter is unexpected but implemented not to crush because of bad output from parsers. type UnreachableSortStrategy struct { } // SortChunk implements ResourceChunkSortStrategy. func (u *UnreachableSortStrategy) SortChunk(builder *Builder, parents []*Resource, groupedRelationship enum.ParentRelationship, chunk []*Resource) ([]*Resource, error) { cloned := slices.Clone(chunk) slices.SortFunc(cloned, func(a, b *Resource) int { return strings.Compare(a.ResourceName, b.ResourceName) }) for _, c := range chunk { slog.Warn(fmt.Sprintf("hitting unreachable sorter: %s", c.FullResourcePath)) } return cloned, nil } var _ ResourceChunkSortStrategy = (*UnreachableSortStrategy)(nil) type FirstRevisionTimeSortStrategy struct { TargetRelationship enum.ParentRelationship } // SortChunk implements ResourceChunkSortStrategy. func (b *FirstRevisionTimeSortStrategy) SortChunk(builder *Builder, parents []*Resource, groupedRelationship enum.ParentRelationship, chunk []*Resource) ([]*Resource, error) { if groupedRelationship != b.TargetRelationship { return nil, ErrorSortSkipped } cloned := slices.Clone(chunk) slices.SortFunc(cloned, func(a, b *Resource) int { abuilder := builder.GetTimelineBuilder(a.FullResourcePath) bbuilder := builder.GetTimelineBuilder(b.FullResourcePath) arevs := abuilder.timeline.Revisions brevs := bbuilder.timeline.Revisions if len(arevs) == 0 { return -1 } if len(brevs) == 0 { return 1 } return int(arevs[0].ChangeTime.Sub(brevs[0].ChangeTime)) }) return cloned, nil } var _ ResourceChunkSortStrategy = (*FirstRevisionTimeSortStrategy)(nil)