1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
15
"launchpad.net/gnuflag"
16
"launchpad.net/goyaml"
19
// Formatter converts an arbitrary object into a []byte.
20
type Formatter func(value interface{}) ([]byte, error)
22
// FormatYaml marshals value to a yaml-formatted []byte, unless value is nil.
23
func FormatYaml(value interface{}) ([]byte, error) {
27
result, err := goyaml.Marshal(value)
31
for i := len(result) - 1; i > 0; i-- {
32
if result[i] != '\n' {
40
// FormatJson marshals value to a json-formatted []byte.
41
var FormatJson = json.Marshal
43
// FormatSmart marshals value into a []byte according to the following rules:
44
// * string: untouched
45
// * bool: converted to `True` or `False` (to match pyjuju)
46
// * int or float: converted to sensible strings
47
// * []string: joined by `\n`s into a single string
48
// * anything else: delegate to FormatYaml
49
func FormatSmart(value interface{}) ([]byte, error) {
53
v := reflect.ValueOf(value)
54
switch kind := v.Kind(); kind {
56
return []byte(value.(string)), nil
57
case reflect.Array, reflect.Slice:
58
if v.Type().Elem().Kind() == reflect.String {
59
return []byte(strings.Join(value.([]string), "\n")), nil
63
return []byte("True"), nil
65
return []byte("False"), nil
66
case reflect.Map, reflect.Float32, reflect.Float64:
67
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
68
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
70
return nil, fmt.Errorf("cannot marshal %#v", value)
72
return FormatYaml(value)
75
// DefaultFormatters holds the formatters that can be
76
// specified with the --format flag.
77
var DefaultFormatters = map[string]Formatter{
83
// formatterValue implements gnuflag.Value for the --format flag.
84
type formatterValue struct {
86
formatters map[string]Formatter
89
// newFormatterValue returns a new formatterValue. The initial Formatter name
90
// must be present in formatters.
91
func newFormatterValue(initial string, formatters map[string]Formatter) *formatterValue {
92
v := &formatterValue{formatters: formatters}
93
if err := v.Set(initial); err != nil {
99
// Set stores the chosen formatter name in v.name.
100
func (v *formatterValue) Set(value string) error {
101
if v.formatters[value] == nil {
102
return fmt.Errorf("unknown format %q", value)
108
// String returns the chosen formatter name.
109
func (v *formatterValue) String() string {
113
// doc returns documentation for the --format flag.
114
func (v *formatterValue) doc() string {
115
choices := make([]string, len(v.formatters))
117
for name := range v.formatters {
121
sort.Strings(choices)
122
return "specify output format (" + strings.Join(choices, "|") + ")"
125
// format runs the chosen formatter on value.
126
func (v *formatterValue) format(value interface{}) ([]byte, error) {
127
return v.formatters[v.name](value)
130
// Output is responsible for interpreting output-related command line flags
131
// and writing a value to a file or to stdout as directed.
133
formatter *formatterValue
137
// AddFlags injects the --format and --output command line flags into f.
138
func (c *Output) AddFlags(f *gnuflag.FlagSet, defaultFormatter string, formatters map[string]Formatter) {
139
c.formatter = newFormatterValue(defaultFormatter, formatters)
140
f.Var(c.formatter, "format", c.formatter.doc())
141
f.StringVar(&c.outPath, "o", "", "specify an output file")
142
f.StringVar(&c.outPath, "output", "", "")
145
// Write formats and outputs the value as directed by the --format and
146
// --output command line flags.
147
func (c *Output) Write(ctx *Context, value interface{}) (err error) {
152
path := ctx.AbsPath(c.outPath)
153
if target, err = os.Create(path); err != nil {
157
bytes, err := c.formatter.format(value)
162
_, err = target.Write(bytes)
164
_, err = target.Write([]byte{'\n'})
170
func (c *Output) Name() string {
171
return c.formatter.name