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  }