github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/resource/cmd/output_tabular.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package cmd 5 6 import ( 7 "bytes" 8 "fmt" 9 "sort" 10 "text/tabwriter" 11 12 "github.com/juju/errors" 13 ) 14 15 // FormatCharmTabular returns a tabular summary of charm resources. 16 func FormatCharmTabular(value interface{}) ([]byte, error) { 17 resources, valueConverted := value.([]FormattedCharmResource) 18 if !valueConverted { 19 return nil, errors.Errorf("expected value of type %T, got %T", resources, value) 20 } 21 22 // TODO(ericsnow) sort the rows first? 23 24 var out bytes.Buffer 25 26 // To format things into columns. 27 tw := tabwriter.NewWriter(&out, 0, 1, 1, ' ', 0) 28 29 // Write the header. 30 // We do not print a section label. 31 fmt.Fprintln(tw, "RESOURCE\tREVISION") 32 33 // Print each info to its own row. 34 for _, res := range resources { 35 // the column headers must be kept in sync with these. 36 fmt.Fprintf(tw, "%s\t%d\n", 37 res.Name, 38 res.Revision, 39 ) 40 } 41 tw.Flush() 42 43 return out.Bytes(), nil 44 } 45 46 // FormatSvcTabular returns a tabular summary of resources. 47 func FormatSvcTabular(value interface{}) ([]byte, error) { 48 switch resources := value.(type) { 49 case FormattedServiceInfo: 50 return formatServiceTabular(resources), nil 51 case []FormattedUnitResource: 52 return formatUnitTabular(resources), nil 53 case FormattedServiceDetails: 54 return formatServiceDetailTabular(resources), nil 55 case FormattedUnitDetails: 56 return formatUnitDetailTabular(resources), nil 57 default: 58 return nil, errors.Errorf("unexpected type for data: %T", resources) 59 } 60 } 61 62 func formatServiceTabular(info FormattedServiceInfo) []byte { 63 // TODO(ericsnow) sort the rows first? 64 65 var out bytes.Buffer 66 67 fmt.Fprintln(&out, "[Service]") 68 tw := tabwriter.NewWriter(&out, 0, 1, 1, ' ', 0) 69 fmt.Fprintln(tw, "RESOURCE\tSUPPLIED BY\tREVISION") 70 71 // Print each info to its own row. 72 for _, r := range info.Resources { 73 // the column headers must be kept in sync with these. 74 fmt.Fprintf(tw, "%v\t%v\t%v\n", 75 r.Name, 76 r.combinedOrigin, 77 r.combinedRevision, 78 ) 79 } 80 81 // Don't forget to flush! The Tab writer won't actually write to the output 82 // until you flush, which would then have its output incorrectly ordered 83 // with the below fmt.Fprintlns. 84 tw.Flush() 85 86 writeUpdates(info.Updates, &out, tw) 87 88 return out.Bytes() 89 } 90 91 func writeUpdates(updates []FormattedCharmResource, out *bytes.Buffer, tw *tabwriter.Writer) { 92 if len(updates) > 0 { 93 fmt.Fprintln(out, "") 94 fmt.Fprintln(out, "[Updates Available]") 95 fmt.Fprintln(tw, "RESOURCE\tREVISION") 96 for _, r := range updates { 97 fmt.Fprintf(tw, "%v\t%v\n", 98 r.Name, 99 r.Revision, 100 ) 101 } 102 } 103 104 tw.Flush() 105 } 106 107 func formatUnitTabular(resources []FormattedUnitResource) []byte { 108 // TODO(ericsnow) sort the rows first? 109 110 var out bytes.Buffer 111 112 fmt.Fprintln(&out, "[Unit]") 113 114 // To format things into columns. 115 tw := tabwriter.NewWriter(&out, 0, 1, 1, ' ', 0) 116 117 // Write the header. 118 // We do not print a section label. 119 fmt.Fprintln(tw, "RESOURCE\tREVISION") 120 121 // Print each info to its own row. 122 for _, r := range resources { 123 // the column headers must be kept in sync with these. 124 fmt.Fprintf(tw, "%v\t%v\n", 125 r.Name, 126 r.combinedRevision, 127 ) 128 } 129 tw.Flush() 130 131 return out.Bytes() 132 } 133 134 func formatServiceDetailTabular(resources FormattedServiceDetails) []byte { 135 // note that the unit resource can be a zero value here, to indicate that 136 // the unit has not downloaded that resource yet. 137 138 var out bytes.Buffer 139 fmt.Fprintln(&out, "[Units]") 140 141 sort.Sort(byUnitID(resources.Resources)) 142 // To format things into columns. 143 tw := tabwriter.NewWriter(&out, 0, 1, 1, ' ', 0) 144 145 // Write the header. 146 fmt.Fprintln(tw, "UNIT\tRESOURCE\tREVISION\tEXPECTED") 147 148 for _, r := range resources.Resources { 149 fmt.Fprintf(tw, "%v\t%v\t%v\t%v\n", 150 r.unitNumber, 151 r.Expected.Name, 152 r.Unit.combinedRevision, 153 r.revProgress, 154 ) 155 } 156 tw.Flush() 157 158 writeUpdates(resources.Updates, &out, tw) 159 160 return out.Bytes() 161 } 162 163 func formatUnitDetailTabular(resources FormattedUnitDetails) []byte { 164 // note that the unit resource can be a zero value here, to indicate that 165 // the unit has not downloaded that resource yet. 166 167 var out bytes.Buffer 168 fmt.Fprintln(&out, "[Unit]") 169 170 sort.Sort(byUnitID(resources)) 171 // To format things into columns. 172 tw := tabwriter.NewWriter(&out, 0, 1, 1, ' ', 0) 173 174 // Write the header. 175 fmt.Fprintln(tw, "RESOURCE\tREVISION\tEXPECTED") 176 177 for _, r := range resources { 178 fmt.Fprintf(tw, "%v\t%v\t%v\n", 179 r.Expected.Name, 180 r.Unit.combinedRevision, 181 r.revProgress, 182 ) 183 } 184 tw.Flush() 185 return out.Bytes() 186 } 187 188 type byUnitID []FormattedDetailResource 189 190 func (b byUnitID) Len() int { return len(b) } 191 func (b byUnitID) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 192 193 func (b byUnitID) Less(i, j int) bool { 194 if b[i].unitNumber != b[j].unitNumber { 195 return b[i].unitNumber < b[j].unitNumber 196 } 197 return b[i].Expected.Name < b[j].Expected.Name 198 }