Sort Order for tasks #110

Merged
konrad merged 62 commits from feature/sort-order into master 2019-12-07 14:30:52 +00:00
1 changed files with 66 additions and 94 deletions
Showing only changes of commit ddd739f393 - Show all commits

View File

@ -16,7 +16,11 @@
package models
import "sort"
import (
"fmt"
"reflect"
"sort"
)
type (
sortParam struct {
@ -101,117 +105,85 @@ func (sp *sortParam) validate() error {
return ErrInvalidSortParam{SortBy: sp.sortBy}
}
type taskComparator func(lhs, rhs *Task) int
type taskComparator func(lhs, rhs *Task) int64
// TODO: Move somewhere else
func stringCompare(lhs, rhs string) int {
if lhs > rhs {
return 1
func mustMakeComparator(fieldName string) taskComparator {
field, ok := reflect.TypeOf(&Task{}).Elem().FieldByName(fieldName)
if !ok {
panic(fmt.Sprintf("Field '%s' has not been found on Task", fieldName))
}
if lhs < rhs {
return -1
}
return 0
}
func int64Compare(lhs, rhs int64) int {
if lhs > rhs {
return 1
extractProp := func(task *Task) interface{} {
return reflect.ValueOf(task).Elem().FieldByIndex(field.Index).Interface()
}
if lhs < rhs {
return -1
}
return 0
}
func float64Compare(lhs, rhs float64) int {
if lhs > rhs {
return 1
}
if lhs < rhs {
return -1
}
return 0
}
func boolCompare(lhs, rhs, first bool) int {
// TODO: Can we simplify this?
if !lhs && rhs {
if first {
return 1
switch field.Type.Kind() {
case reflect.Int64:
return func(lhs, rhs *Task) int64 {
return extractProp(lhs).(int64) - extractProp(rhs).(int64)
}
return -1
}
if lhs && !rhs {
if first {
return -1
case reflect.Float64:
return func(lhs, rhs *Task) int64 {
floatLhs, floatRhs := extractProp(lhs).(float64), extractProp(rhs).(float64)
if floatLhs > floatRhs {
return 1
} else if floatLhs < floatRhs {
return -1
}
return 0
}
return 1
case reflect.String:
return func(lhs, rhs *Task) int64 {
strLhs, strRhs := extractProp(lhs).(string), extractProp(rhs).(string)
if strLhs > strRhs {
return 1
} else if strLhs < strRhs {
return -1
}
return 0
}
case reflect.Bool:
return func(lhs, rhs *Task) int64 {
boolLhs, boolRhs := extractProp(lhs).(bool), extractProp(rhs).(bool)
if boolLhs && !boolRhs {
return 1
} else if !boolLhs && boolRhs {
return -1
}
return 0
}
default:
panic(fmt.Sprintf("Unsupported type for sorting: %s", field.Type.Name()))
}
return 0
}
// This is a map of properties that can be sorted by
// and their appropriate comparator function.
// The comparator function sorts in ascending mode.
var propertyComparators = map[sortProperty]taskComparator{
taskPropertyText: func(lhs, rhs *Task) int {
return stringCompare(lhs.Text, rhs.Text)
},
taskPropertyDescription: func(lhs, rhs *Task) int {
return stringCompare(lhs.Description, rhs.Description)
},
taskPropertyDone: func(lhs, rhs *Task) int {
return boolCompare(lhs.Done, rhs.Done, true)
},
taskPropertyDoneAtUnix: func(lhs, rhs *Task) int {
return int64Compare(lhs.DoneAtUnix, rhs.DoneAtUnix)
},
taskPropertyDueDateUnix: func(lhs, rhs *Task) int {
return int64Compare(lhs.DueDateUnix, rhs.DueDateUnix)
},
taskPropertyCreatedByID: func(lhs, rhs *Task) int {
return int64Compare(lhs.CreatedByID, rhs.CreatedByID)
},
taskPropertyListID: func(lhs, rhs *Task) int {
return int64Compare(lhs.ListID, rhs.ListID)
},
taskPropertyRepeatAfter: func(lhs, rhs *Task) int {
return int64Compare(lhs.RepeatAfter, rhs.RepeatAfter)
},
taskPropertyPriority: func(lhs, rhs *Task) int {
return int64Compare(lhs.Priority, rhs.Priority)
},
taskPropertyStartDateUnix: func(lhs, rhs *Task) int {
return int64Compare(lhs.StartDateUnix, rhs.StartDateUnix)
},
taskPropertyEndDateUnix: func(lhs, rhs *Task) int {
return int64Compare(lhs.EndDateUnix, rhs.EndDateUnix)
},
taskPropertyHexColor: func(lhs, rhs *Task) int {
return stringCompare(lhs.HexColor, rhs.HexColor)
},
taskPropertyPercentDone: func(lhs, rhs *Task) int {
return float64Compare(lhs.PercentDone, rhs.PercentDone)
},
taskPropertyUID: func(lhs, rhs *Task) int {
return stringCompare(lhs.UID, rhs.UID)
},
taskPropertyCreated: func(lhs, rhs *Task) int {
return int64Compare(lhs.Created, rhs.Created)
},
taskPropertyUpdated: func(lhs, rhs *Task) int {
return int64Compare(lhs.Updated, rhs.Updated)
},
taskPropertyID: func(lhs, rhs *Task) int {
return int64Compare(lhs.ID, rhs.ID)
},
taskPropertyText: mustMakeComparator("Text"),
taskPropertyDescription: mustMakeComparator("Description"),
taskPropertyDone: mustMakeComparator("Done"),
taskPropertyDoneAtUnix: mustMakeComparator("DoneAtUnix"),
taskPropertyDueDateUnix: mustMakeComparator("DueDateUnix"),
taskPropertyCreatedByID: mustMakeComparator("CreatedByID"),
taskPropertyListID: mustMakeComparator("ListID"),
taskPropertyRepeatAfter: mustMakeComparator("RepeatAfter"),
taskPropertyPriority: mustMakeComparator("Priority"),
taskPropertyStartDateUnix: mustMakeComparator("StartDateUnix"),
taskPropertyEndDateUnix: mustMakeComparator("EndDateUnix"),
taskPropertyHexColor: mustMakeComparator("HexColor"),
taskPropertyPercentDone: mustMakeComparator("PercentDone"),
taskPropertyUID: mustMakeComparator("UID"),
taskPropertyCreated: mustMakeComparator("Created"),
taskPropertyUpdated: mustMakeComparator("Updated"),
taskPropertyID: mustMakeComparator("ID"),
}
// Creates a taskComparator that sorts by the first comparator and falls back to
// the second one (and so on...) if the properties were equal.
func combineComparators(comparators ...taskComparator) taskComparator {
return func(lhs, rhs *Task) int {
return func(lhs, rhs *Task) int64 {
for _, compare := range comparators {
res := compare(lhs, rhs)
if res != 0 {
@ -239,7 +211,7 @@ func sortTasks(tasks []*Task, by []*sortParam) {
// This is a descending sort, so we need to negate the comparator (i.e. switch the inputs).
if param.orderBy == orderDescending {
oldComparator := comparator
comparator = func(lhs, rhs *Task) int {
comparator = func(lhs, rhs *Task) int64 {
return oldComparator(lhs, rhs) * -1
}
}