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