Sort Order for tasks #110
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue