github.com/MontFerret/ferret@v0.18.0/pkg/runtime/collections/sort.go (about) 1 package collections 2 3 import ( 4 "context" 5 "sort" 6 "strings" 7 8 "github.com/MontFerret/ferret/pkg/runtime/core" 9 ) 10 11 type ( 12 SortDirection int 13 14 Comparator func(ctx context.Context, first, second *core.Scope) (int64, error) 15 16 Sorter struct { 17 fn Comparator 18 direction SortDirection 19 } 20 21 SortIterator struct { 22 values Iterator 23 sorters []*Sorter 24 ready bool 25 result []*core.Scope 26 pos int 27 } 28 ) 29 30 const ( 31 SortDirectionAsc SortDirection = 1 32 SortDirectionDesc SortDirection = -1 33 ) 34 35 func SortDirectionFromString(str string) SortDirection { 36 if strings.EqualFold(str, "DESC") { 37 return SortDirectionDesc 38 } 39 40 return SortDirectionAsc 41 } 42 43 func IsValidSortDirection(direction SortDirection) bool { 44 switch direction { 45 case SortDirectionAsc, SortDirectionDesc: 46 return true 47 default: 48 return false 49 } 50 } 51 52 func NewSorter(fn Comparator, direction SortDirection) (*Sorter, error) { 53 if fn == nil { 54 return nil, core.Error(core.ErrMissedArgument, "fn") 55 } 56 57 if !IsValidSortDirection(direction) { 58 return nil, core.Error(core.ErrInvalidArgument, "direction") 59 } 60 61 return &Sorter{fn, direction}, nil 62 } 63 64 func NewSortIterator( 65 values Iterator, 66 comparators ...*Sorter, 67 ) (*SortIterator, error) { 68 if values == nil { 69 return nil, core.Error(core.ErrMissedArgument, "values") 70 } 71 72 if len(comparators) == 0 { 73 return nil, core.Error(core.ErrMissedArgument, "comparator") 74 } 75 76 return &SortIterator{ 77 values, 78 comparators, 79 false, 80 nil, 81 0, 82 }, nil 83 } 84 85 func (iterator *SortIterator) Next(ctx context.Context, scope *core.Scope) (*core.Scope, error) { 86 // we need to initialize the iterator 87 if !iterator.ready { 88 iterator.ready = true 89 sorted, err := iterator.sort(ctx, scope) 90 91 if err != nil { 92 return nil, err 93 } 94 95 iterator.result = sorted 96 } 97 98 if len(iterator.result) > iterator.pos { 99 idx := iterator.pos 100 val := iterator.result[idx] 101 102 iterator.pos++ 103 104 return val, nil 105 } 106 107 return nil, core.ErrNoMoreData 108 } 109 110 func (iterator *SortIterator) sort(ctx context.Context, scope *core.Scope) ([]*core.Scope, error) { 111 scopes, err := ToSlice(ctx, scope, iterator.values) 112 113 if err != nil { 114 return nil, err 115 } 116 117 var failure error 118 119 sort.SliceStable(scopes, func(i, j int) bool { 120 // ignore next execution 121 if failure != nil { 122 return false 123 } 124 125 var out bool 126 127 for _, comp := range iterator.sorters { 128 left := scopes[i] 129 right := scopes[j] 130 131 eq, err := comp.fn(ctx, left, right) 132 133 if err != nil { 134 failure = err 135 out = false 136 137 break 138 } 139 140 eq *= int64(comp.direction) 141 142 if eq == -1 { 143 out = true 144 break 145 } 146 147 if eq == 1 { 148 out = false 149 break 150 } 151 } 152 153 return out 154 }) 155 156 if failure != nil { 157 return nil, failure 158 } 159 160 return scopes, nil 161 }