aboutsummaryrefslogtreecommitdiff
path: root/gui/internal/model/sort.go
diff options
context:
space:
mode:
Diffstat (limited to 'gui/internal/model/sort.go')
-rw-r--r--gui/internal/model/sort.go84
1 files changed, 84 insertions, 0 deletions
diff --git a/gui/internal/model/sort.go b/gui/internal/model/sort.go
new file mode 100644
index 0000000..9968a08
--- /dev/null
+++ b/gui/internal/model/sort.go
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package model
+
+import (
+ "sort"
+ "strings"
+)
+
+// The orders a plan can be read in. "default" is the order krino planned
+// the directory in, which is the order the files were scanned; the rest are
+// what a column is worth sorting by (his request, 2026-09-17).
+const (
+ SortDefault = "default"
+ SortName = "name"
+ SortSize = "size"
+ SortAge = "age"
+ SortAction = "action"
+ SortRule = "rule"
+)
+
+// SortOrders are the choices, in the order they are offered.
+var SortOrders = []string{SortDefault, SortName, SortSize, SortAge, SortAction, SortRule}
+
+// SortLabels say which way each order runs, for the picker and its help.
+var SortLabels = map[string]string{
+ SortDefault: "as scanned",
+ SortName: "name, A to Z",
+ SortSize: "size, largest first",
+ SortAge: "age, oldest first",
+ SortAction: "action, A to Z",
+ SortRule: "rule, A to Z",
+}
+
+// IsSortOrder reports whether s names one of them.
+func IsSortOrder(s string) bool {
+ for _, o := range SortOrders {
+ if o == s {
+ return true
+ }
+ }
+ return false
+}
+
+// Sorted puts the rows named by shown in the order asked for, leaving the
+// plan itself alone: only the reading order changes. Ties keep the plan's
+// own order, so the list never shuffles between two files that compare the
+// same.
+func (t *PlanTab) Sorted(shown []int, by string) []int {
+ out := append([]int(nil), shown...)
+ if by == SortDefault || by == "" || !IsSortOrder(by) {
+ return out
+ }
+ less := func(a, b int) bool {
+ x, y := t.Rows[a], t.Rows[b]
+ switch by {
+ case SortName:
+ return strings.ToLower(x.Rel) < strings.ToLower(y.Rel)
+ case SortSize:
+ return x.Size > y.Size
+ case SortAge:
+ // Oldest first: the files an (age > ...) rule is about.
+ return x.ModTime.Before(y.ModTime)
+ case SortAction:
+ return firstAction(x) < firstAction(y)
+ case SortRule:
+ return strings.ToLower(x.Rule) < strings.ToLower(y.Rule)
+ }
+ return false
+ }
+ sort.SliceStable(out, func(i, j int) bool { return less(out[i], out[j]) })
+ return out
+}
+
+// firstAction is what a row does, for sorting; a row that does nothing
+// sorts after the ones that do.
+func firstAction(r Row) string {
+ for _, s := range r.Steps {
+ if s.Skip == "" {
+ return s.Kind.String()
+ }
+ }
+ return "~"
+}