Files
kandev/apps/backend/internal/linear/issue_sort.go
T

80 lines
2.7 KiB
Go

package linear
import (
"sort"
"time"
)
// sortIssues reorders matched issues in place so the highest-priority /
// most-relevant issues are published (and therefore dispatched) first under
// the watch's in-flight cap. An empty/unknown sort key leaves the slice in the
// order Linear returned it (updatedAt asc). Stable so equal-key issues keep
// Linear's relative order.
func sortIssues(issues []*LinearIssue, by IssueSortBy) {
switch by {
case SortByPriorityDesc:
sort.SliceStable(issues, func(i, j int) bool {
return priorityRank(issues[i].Priority) < priorityRank(issues[j].Priority)
})
case SortByPriorityAsc:
sort.SliceStable(issues, func(i, j int) bool {
return priorityRank(issues[i].Priority) > priorityRank(issues[j].Priority)
})
case SortByCreatedDesc:
sort.SliceStable(issues, func(i, j int) bool {
return timeLess(parseLinearTime(issues[i].Created), parseLinearTime(issues[j].Created), false)
})
case SortByCreatedAsc:
sort.SliceStable(issues, func(i, j int) bool {
return timeLess(parseLinearTime(issues[i].Created), parseLinearTime(issues[j].Created), true)
})
case SortByUpdatedDesc:
sort.SliceStable(issues, func(i, j int) bool {
return timeLess(parseLinearTime(issues[i].Updated), parseLinearTime(issues[j].Updated), false)
})
case SortByUpdatedAsc:
sort.SliceStable(issues, func(i, j int) bool {
return timeLess(parseLinearTime(issues[i].Updated), parseLinearTime(issues[j].Updated), true)
})
}
}
// timeLess orders two Linear timestamps for the date sort modes. Valid times
// order normally (ascending or descending per `asc`); an invalid/zero time
// always sorts AFTER a valid one so malformed values never take the earliest
// dispatch slots. Two invalids keep their input order (stable sort).
func timeLess(a, b time.Time, asc bool) bool {
if a.IsZero() || b.IsZero() {
return !a.IsZero() && b.IsZero() // valid before invalid; else keep order
}
if asc {
return a.Before(b)
}
return a.After(b)
}
// priorityRank maps Linear's priority encoding (0=none,1=urgent,2=high,
// 3=medium,4=low) onto an importance rank where LOWER = more important:
// urgent(1) < high(2) < medium(3) < low(4) < none. "None" (0) is treated as
// least important, so it ranks after low rather than before urgent.
func priorityRank(p int) int {
if p == 0 {
return 5
}
return p
}
// parseLinearTime parses a Linear ISO-8601 timestamp, returning the zero time
// on empty/unparseable input. timeLess treats that zero time as "sort last"
// in both directions so malformed values never grab the earliest slots.
func parseLinearTime(s string) time.Time {
if s == "" {
return time.Time{}
}
t, err := time.Parse(time.RFC3339, s)
if err != nil {
return time.Time{}
}
return t
}