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