1// Copyright 2015 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package drive
16
17import (
18	"sort"
19	"strings"
20)
21
22const (
23	AttrUnknown = iota
24	AttrSize
25	AttrModTime
26	AttrLastViewedByMeTime
27	AttrVersion
28	AttrIsDir
29	AttrMd5Checksum
30	AttrMimeType
31	AttrName
32)
33
34type attr int
35
36type fileList []*File
37type lastViewedTimeFlist fileList
38type md5Flist fileList
39type mimeTypeFlist fileList
40type modTimeFlist fileList
41type nameFlist fileList
42type sizeFlist fileList
43type typeFlist fileList
44type versionFlist fileList
45
46var (
47	lastViewedTimeCmpLess = _lessCmper(AttrLastViewedByMeTime)
48	md5CmpLess            = _lessCmper(AttrMd5Checksum)
49	mimeTypeCmpLess       = _lessCmper(AttrMimeType)
50	modTimeCmpLess        = _lessCmper(AttrModTime)
51	nameCmpLess           = _lessCmper(AttrName)
52	sizeCmpLess           = _lessCmper(AttrSize)
53	versionCmpLess        = _lessCmper(AttrVersion)
54	typeCmpLess           = _lessCmper(AttrIsDir)
55)
56
57func (fl modTimeFlist) Less(i, j int) bool {
58	return modTimeCmpLess(fl[i], fl[j])
59}
60
61func (fl modTimeFlist) Len() int {
62	return len(fl)
63}
64
65func (fl modTimeFlist) Swap(i, j int) {
66	fl[i], fl[j] = fl[j], fl[i]
67}
68
69func (fl typeFlist) Less(i, j int) bool {
70	return typeCmpLess(fl[i], fl[j])
71}
72
73func (fl typeFlist) Len() int {
74	return len(fl)
75}
76
77func (fl typeFlist) Swap(i, j int) {
78	fl[i], fl[j] = fl[j], fl[i]
79}
80
81func (fl mimeTypeFlist) Less(i, j int) bool {
82	return mimeTypeCmpLess(fl[i], fl[j])
83}
84
85func (fl mimeTypeFlist) Len() int {
86	return len(fl)
87}
88
89func (fl mimeTypeFlist) Swap(i, j int) {
90	fl[i], fl[j] = fl[j], fl[i]
91}
92
93func (fl lastViewedTimeFlist) Less(i, j int) bool {
94	return lastViewedTimeCmpLess(fl[i], fl[j])
95}
96
97func (fl lastViewedTimeFlist) Len() int {
98	return len(fl)
99}
100
101func (fl lastViewedTimeFlist) Swap(i, j int) {
102	fl[i], fl[j] = fl[j], fl[i]
103}
104
105func (fl versionFlist) Less(i, j int) bool {
106	return versionCmpLess(fl[i], fl[j])
107}
108
109func (fl versionFlist) Len() int {
110	return len(fl)
111}
112
113func (fl versionFlist) Swap(i, j int) {
114	fl[i], fl[j] = fl[j], fl[i]
115}
116
117func (fl nameFlist) Less(i, j int) bool {
118	return nameCmpLess(fl[i], fl[j])
119}
120
121func (fl nameFlist) Len() int {
122	return len(fl)
123}
124
125func (fl nameFlist) Swap(i, j int) {
126	fl[i], fl[j] = fl[j], fl[i]
127}
128
129func (fl md5Flist) Less(i, j int) bool {
130	return md5CmpLess(fl[i], fl[j])
131}
132
133func (fl md5Flist) Len() int {
134	return len(fl)
135}
136
137func (fl md5Flist) Swap(i, j int) {
138	fl[i], fl[j] = fl[j], fl[i]
139}
140
141func (fl sizeFlist) Less(i, j int) bool {
142	return sizeCmpLess(fl[i], fl[j])
143}
144
145func (fl sizeFlist) Len() int {
146	return len(fl)
147}
148
149func (fl sizeFlist) Swap(i, j int) {
150	fl[i], fl[j] = fl[j], fl[i]
151}
152
153func attrAtoiSorter(a string, fl []*File) (attr, sort.Interface, bool) {
154	aLower := strings.ToLower(a)
155	if len(aLower) < 1 {
156		return AttrUnknown, nil, false
157	}
158
159	reverse := hasAnySuffix(aLower, "_r", "-")
160
161	if hasAnyPrefix(aLower, Md5Key) {
162		return AttrMd5Checksum, md5Flist(fl), reverse
163	}
164	if hasAnyPrefix(aLower, NameKey) {
165		return AttrName, nameFlist(fl), reverse
166	}
167	if hasAnyPrefix(aLower, SizeKey) {
168		return AttrSize, sizeFlist(fl), reverse
169	}
170	if hasAnyPrefix(aLower, TypeKey) {
171		return AttrIsDir, typeFlist(fl), reverse
172	}
173	if hasAnyPrefix(aLower, ModTimeKey) {
174		return AttrModTime, modTimeFlist(fl), reverse
175	}
176	if hasAnyPrefix(aLower, LastViewedByMeTimeKey) {
177		return AttrLastViewedByMeTime, lastViewedTimeFlist(fl), reverse
178	}
179	if hasAnyPrefix(aLower, VersionKey) {
180		return AttrVersion, versionFlist(fl), reverse
181	}
182
183	return AttrUnknown, nil, false
184}
185
186func nilCmpOrProceed(fallback func(*File, *File) bool) func(*File, *File) bool {
187	return func(l, r *File) bool {
188		if l == nil {
189			return false
190		}
191		if r == nil {
192			return true
193		}
194		return fallback(l, r)
195	}
196}
197
198func _lessCmper(_attr attr) func(*File, *File) bool {
199	switch _attr {
200	case AttrSize:
201		return nilCmpOrProceed(func(l, r *File) bool { return l.Size < r.Size })
202	case AttrVersion:
203		return nilCmpOrProceed(func(l, r *File) bool { return l.Version < r.Version })
204	case AttrIsDir:
205		return nilCmpOrProceed(func(l, r *File) bool { return !l.IsDir })
206	case AttrMd5Checksum:
207		return nilCmpOrProceed(func(l, r *File) bool { return l.Md5Checksum < r.Md5Checksum })
208	case AttrName:
209		return nilCmpOrProceed(func(l, r *File) bool { return l.Name < r.Name })
210	case AttrModTime:
211		return nilCmpOrProceed(func(l, r *File) bool { return l.ModTime.Before(r.ModTime) })
212	case AttrLastViewedByMeTime:
213		return nilCmpOrProceed(func(l, r *File) bool { return l.LastViewedByMeTime.Before(r.LastViewedByMeTime) })
214	}
215
216	return nilCmpOrProceed(func(l, r *File) bool { return true })
217}
218
219func (g *Commands) sort(fl []*File, attrStrValues ...string) []*File {
220	for _, attrStr := range attrStrValues {
221		attrEnum, sortInterface, reverse := attrAtoiSorter(attrStr, fl)
222
223		if attrEnum == AttrUnknown {
224			g.log.LogErrf("%s is an unknown sort attribute\n", attrStr)
225			continue
226		}
227
228		if reverse {
229			sortInterface = sort.Reverse(sortInterface)
230		}
231		// Stable is needed if more than one sort keyword is used
232		sort.Stable(sortInterface)
233	}
234
235	return fl
236}
237