80 lines
2.7 KiB
Go
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
|
|
}
|