github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/storage/listformatters.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storage 5 6 import ( 7 "bytes" 8 "fmt" 9 "sort" 10 "strconv" 11 "strings" 12 "text/tabwriter" 13 14 "github.com/juju/errors" 15 ) 16 17 // formatListTabular returns a tabular summary of storage instances. 18 func formatStorageListTabular(value interface{}) ([]byte, error) { 19 storageInfo, ok := value.(map[string]StorageInfo) 20 if !ok { 21 return nil, errors.Errorf("expected value of type %T, got %T", storageInfo, value) 22 } 23 var out bytes.Buffer 24 // To format things into columns. 25 tw := tabwriter.NewWriter(&out, 0, 1, 1, ' ', 0) 26 p := func(values ...interface{}) { 27 for _, v := range values { 28 fmt.Fprintf(tw, "%v\t", v) 29 } 30 fmt.Fprintln(tw) 31 } 32 p("[Storage]") 33 p("UNIT\tID\tLOCATION\tSTATUS\tMESSAGE") 34 35 byUnit := make(map[string]map[string]storageAttachmentInfo) 36 for storageId, storageInfo := range storageInfo { 37 if storageInfo.Attachments == nil { 38 byStorage := byUnit[""] 39 if byStorage == nil { 40 byStorage = make(map[string]storageAttachmentInfo) 41 byUnit[""] = byStorage 42 } 43 byStorage[storageId] = storageAttachmentInfo{ 44 storageId: storageId, 45 kind: storageInfo.Kind, 46 persistent: storageInfo.Persistent, 47 status: storageInfo.Status, 48 } 49 continue 50 } 51 for unitId, a := range storageInfo.Attachments.Units { 52 byStorage := byUnit[unitId] 53 if byStorage == nil { 54 byStorage = make(map[string]storageAttachmentInfo) 55 byUnit[unitId] = byStorage 56 } 57 byStorage[storageId] = storageAttachmentInfo{ 58 storageId: storageId, 59 unitId: unitId, 60 kind: storageInfo.Kind, 61 persistent: storageInfo.Persistent, 62 location: a.Location, 63 status: storageInfo.Status, 64 } 65 } 66 } 67 68 // First sort by units 69 units := make([]string, 0, len(storageInfo)) 70 for unit := range byUnit { 71 units = append(units, unit) 72 } 73 sort.Strings(slashSeparatedIds(units)) 74 75 for _, unit := range units { 76 // Then sort by storage ids 77 byStorage := byUnit[unit] 78 storageIds := make([]string, 0, len(byStorage)) 79 for storageId := range byStorage { 80 storageIds = append(storageIds, storageId) 81 } 82 sort.Strings(slashSeparatedIds(storageIds)) 83 84 for _, storageId := range storageIds { 85 info := byStorage[storageId] 86 p(info.unitId, info.storageId, info.location, info.status.Current, info.status.Message) 87 } 88 } 89 tw.Flush() 90 91 return out.Bytes(), nil 92 } 93 94 type storageAttachmentInfo struct { 95 storageId string 96 unitId string 97 kind string 98 persistent bool 99 location string 100 status EntityStatus 101 } 102 103 type slashSeparatedIds []string 104 105 func (s slashSeparatedIds) Len() int { 106 return len(s) 107 } 108 109 func (s slashSeparatedIds) Swap(a, b int) { 110 s[a], s[b] = s[b], s[a] 111 } 112 113 func (s slashSeparatedIds) Less(a, b int) bool { 114 return compareSlashSeparated(s[a], s[b]) == -1 115 } 116 117 // compareSlashSeparated compares a with b, first the string before 118 // "/", and then the integer or string after. Empty strings are sorted 119 // after all others. 120 func compareSlashSeparated(a, b string) int { 121 switch { 122 case a == "" && b == "": 123 return 0 124 case a == "": 125 return 1 126 case b == "": 127 return -1 128 } 129 130 sa := strings.SplitN(a, "/", 2) 131 sb := strings.SplitN(b, "/", 2) 132 if sa[0] < sb[0] { 133 return -1 134 } 135 if sa[0] > sb[0] { 136 return 1 137 } 138 139 getInt := func(suffix string) (bool, int) { 140 num, err := strconv.Atoi(suffix) 141 if err != nil { 142 return false, 0 143 } 144 return true, num 145 } 146 147 naIsNumeric, na := getInt(sa[1]) 148 if !naIsNumeric { 149 return compareStrings(sa[1], sb[1]) 150 } 151 nbIsNumeric, nb := getInt(sb[1]) 152 if !nbIsNumeric { 153 return compareStrings(sa[1], sb[1]) 154 } 155 156 switch { 157 case na < nb: 158 return -1 159 case na == nb: 160 return 0 161 } 162 return 1 163 } 164 165 // compareStrings does what strings.Compare does, but without using 166 // strings.Compare as it does not exist in Go 1.2. 167 func compareStrings(a, b string) int { 168 if a == b { 169 return 0 170 } 171 if a < b { 172 return -1 173 } 174 return 1 175 }