Reconstruct and merge ordered inputs without general sorting

This commit is contained in:
2026-11-07 12:10:40 +00:00
parent d72c9a788f
commit 7bb7326ed1
4 changed files with 145 additions and 15 deletions

View File

@@ -1,9 +1,9 @@
package commandkit
import (
"container/heap"
"errors"
"fmt"
"sort"
)
// ErrFlagInput identifies a missing required captured flag selected for an input.
@@ -108,24 +108,74 @@ func (values FlagValues[I]) applyTo(target *I, suppliedOnly bool, selected ...Fl
}
preparedValues = append(preparedValues, prepared)
}
var ordered []preparedOccurrence[I]
for _, prepared := range preparedValues {
var ordered occurrenceHeap[I]
for index, prepared := range preparedValues {
if prepared.apply != nil {
prepared.apply(target)
}
for _, occurrence := range prepared.ordered {
if occurrence.apply != nil {
ordered = append(ordered, occurrence)
}
stream := occurrenceStream[I]{events: prepared.ordered, order: index}
stream.skipUnapplied()
if len(stream.events) > 0 {
ordered = append(ordered, stream)
}
}
sort.SliceStable(ordered, func(i, j int) bool { return *ordered[i].ordinal < *ordered[j].ordinal })
for _, occurrence := range ordered {
occurrence.apply(target)
// One selected stream already has the required order.
if len(ordered) == 1 {
for _, event := range ordered[0].events {
if event.apply != nil {
event.apply(target)
}
}
return nil
}
heap.Init(&ordered)
for len(ordered) > 0 {
stream := &ordered[0]
stream.events[0].apply(target)
stream.events = stream.events[1:]
stream.skipUnapplied()
if len(stream.events) == 0 {
heap.Pop(&ordered)
} else {
heap.Fix(&ordered, 0)
}
}
return nil
}
// Each binding supplies a monotone stream. The selection index breaks ordinal
// ties in the same order as concatenation followed by a stable sort.
type occurrenceStream[I any] struct {
events []preparedOccurrence[I]
order int
}
func (stream *occurrenceStream[I]) skipUnapplied() {
for len(stream.events) > 0 && stream.events[0].apply == nil {
stream.events = stream.events[1:]
}
}
type occurrenceHeap[I any] []occurrenceStream[I]
func (h occurrenceHeap[I]) Len() int { return len(h) }
func (h occurrenceHeap[I]) Less(i, j int) bool {
left, right := *h[i].events[0].ordinal, *h[j].events[0].ordinal
if left == right {
return h[i].order < h[j].order
}
return left < right
}
func (h occurrenceHeap[I]) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *occurrenceHeap[I]) Push(value any) { *h = append(*h, value.(occurrenceStream[I])) }
func (h *occurrenceHeap[I]) Pop() any {
last := len(*h) - 1
value := (*h)[last]
(*h)[last] = occurrenceStream[I]{}
*h = (*h)[:last]
return value
}
func classifyInputError(err error) Outcome {
if errors.Is(err, ErrFlagWiring) {
return Failure(err.Error()).WithCause(err)