1
0
mirror of https://github.com/MontFerret/ferret.git synced 2024-12-23 01:24:30 +02:00
ferret/pkg/runtime/collections/sort.go

162 lines
2.6 KiB
Go
Raw Normal View History

2018-09-18 22:42:38 +02:00
package collections
import (
2018-10-28 07:45:26 +02:00
"context"
2018-09-18 22:42:38 +02:00
"sort"
"strings"
"github.com/MontFerret/ferret/pkg/runtime/core"
2018-09-18 22:42:38 +02:00
)
type (
SortDirection int
Comparator func(ctx context.Context, first, second *core.Scope) (int64, error)
2018-09-18 22:42:38 +02:00
Sorter struct {
fn Comparator
direction SortDirection
}
SortIterator struct {
2018-10-28 07:45:26 +02:00
values Iterator
2018-09-18 22:42:38 +02:00
sorters []*Sorter
ready bool
2018-10-28 07:45:26 +02:00
result []*core.Scope
pos int
2018-09-18 22:42:38 +02:00
}
)
const (
SortDirectionAsc SortDirection = 1
SortDirectionDesc SortDirection = -1
)
func SortDirectionFromString(str string) SortDirection {
if strings.EqualFold(str, "DESC") {
2018-09-18 22:42:38 +02:00
return SortDirectionDesc
}
return SortDirectionAsc
}
func IsValidSortDirection(direction SortDirection) bool {
switch direction {
case SortDirectionAsc, SortDirectionDesc:
return true
default:
return false
}
}
func NewSorter(fn Comparator, direction SortDirection) (*Sorter, error) {
if fn == nil {
return nil, core.Error(core.ErrMissedArgument, "fn")
}
if !IsValidSortDirection(direction) {
2018-09-18 22:42:38 +02:00
return nil, core.Error(core.ErrInvalidArgument, "direction")
}
return &Sorter{fn, direction}, nil
}
func NewSortIterator(
2018-10-28 07:45:26 +02:00
values Iterator,
2018-09-18 22:42:38 +02:00
comparators ...*Sorter,
) (*SortIterator, error) {
2018-10-28 07:45:26 +02:00
if values == nil {
return nil, core.Error(core.ErrMissedArgument, "values")
2018-09-18 22:42:38 +02:00
}
if len(comparators) == 0 {
2018-10-28 07:45:26 +02:00
return nil, core.Error(core.ErrMissedArgument, "comparator")
2018-09-18 22:42:38 +02:00
}
return &SortIterator{
2018-10-28 07:45:26 +02:00
values,
comparators,
false,
2018-10-28 07:45:26 +02:00
nil,
0,
}, nil
2018-09-18 22:42:38 +02:00
}
2018-10-28 07:45:26 +02:00
func (iterator *SortIterator) Next(ctx context.Context, scope *core.Scope) (*core.Scope, error) {
2018-09-18 22:42:38 +02:00
// we need to initialize the iterator
if !iterator.ready {
2018-09-18 22:42:38 +02:00
iterator.ready = true
2018-10-28 07:45:26 +02:00
sorted, err := iterator.sort(ctx, scope)
2018-09-18 22:42:38 +02:00
if err != nil {
2018-10-28 07:45:26 +02:00
return nil, err
2018-09-18 22:42:38 +02:00
}
2018-10-28 07:45:26 +02:00
iterator.result = sorted
}
2018-10-28 07:45:26 +02:00
if len(iterator.result) > iterator.pos {
idx := iterator.pos
2018-10-28 07:45:26 +02:00
val := iterator.result[idx]
iterator.pos++
return val, nil
}
return nil, core.ErrNoMoreData
2018-09-18 22:42:38 +02:00
}
2018-10-28 07:45:26 +02:00
func (iterator *SortIterator) sort(ctx context.Context, scope *core.Scope) ([]*core.Scope, error) {
scopes, err := ToSlice(ctx, scope, iterator.values)
2018-09-18 22:42:38 +02:00
if err != nil {
return nil, err
}
var failure error
2018-10-28 07:45:26 +02:00
sort.SliceStable(scopes, func(i, j int) bool {
2018-09-18 22:42:38 +02:00
// ignore next execution
if failure != nil {
return false
}
var out bool
for _, comp := range iterator.sorters {
2018-10-28 07:45:26 +02:00
left := scopes[i]
right := scopes[j]
2018-09-18 22:42:38 +02:00
2018-10-28 07:45:26 +02:00
eq, err := comp.fn(ctx, left, right)
2018-09-18 22:42:38 +02:00
if err != nil {
failure = err
out = false
break
}
eq *= int64(comp.direction)
2018-09-18 22:42:38 +02:00
if eq == -1 {
out = true
break
}
if eq == 1 {
out = false
break
}
}
return out
})
if failure != nil {
return nil, failure
}
2018-10-28 07:45:26 +02:00
return scopes, nil
2018-09-18 22:42:38 +02:00
}