Reconstruct and merge ordered inputs without general sorting
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user