summaryrefslogtreecommitdiff
path: root/gui/internal/model/sort.go
blob: 191857678bd8e0e69d81d5c9fc04ff82533998a4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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.
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 "~"
}