1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the LGPLv3, see LICENSE file for details.
16
goyaml "gopkg.in/yaml.v2"
17
"launchpad.net/gnuflag"
20
// Formatter converts an arbitrary object into a []byte.
21
type Formatter func(value interface{}) ([]byte, error)
23
// FormatYaml marshals value to a yaml-formatted []byte, unless value is nil.
24
func FormatYaml(value interface{}) ([]byte, error) {
28
result, err := goyaml.Marshal(value)
32
for i := len(result) - 1; i > 0; i-- {
33
if result[i] != '\n' {
41
// FormatJson marshals value to a json-formatted []byte.
42
var FormatJson = json.Marshal
44
// FormatSmart marshals value into a []byte according to the following rules:
45
// * string: untouched
46
// * bool: converted to `True` or `False` (to match pyjuju)
47
// * int or float: converted to sensible strings
48
// * []string: joined by `\n`s into a single string
49
// * anything else: delegate to FormatYaml
50
func FormatSmart(value interface{}) ([]byte, error) {
54
v := reflect.ValueOf(value)
55
switch kind := v.Kind(); kind {
57
return []byte(value.(string)), nil
59
if v.Type().Elem().Kind() == reflect.String {
60
slice := reflect.MakeSlice(reflect.TypeOf([]string(nil)), v.Len(), v.Len())
61
reflect.Copy(slice, v)
62
return []byte(strings.Join(slice.Interface().([]string), "\n")), nil
65
if v.Type().Elem().Kind() == reflect.String {
66
return []byte(strings.Join(value.([]string), "\n")), nil
70
return []byte("True"), nil
72
return []byte("False"), nil
73
case reflect.Float32, reflect.Float64:
74
sv := strconv.FormatFloat(value.(float64), 'f', -1, 64)
75
return []byte(sv), nil
77
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
78
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
80
return nil, fmt.Errorf("cannot marshal %#v", value)
82
return FormatYaml(value)
85
// DefaultFormatters holds the formatters that can be
86
// specified with the --format flag.
87
var DefaultFormatters = map[string]Formatter{
93
// formatterValue implements gnuflag.Value for the --format flag.
94
type formatterValue struct {
96
formatters map[string]Formatter
99
// newFormatterValue returns a new formatterValue. The initial Formatter name
100
// must be present in formatters.
101
func newFormatterValue(initial string, formatters map[string]Formatter) *formatterValue {
102
v := &formatterValue{formatters: formatters}
103
if err := v.Set(initial); err != nil {
109
// Set stores the chosen formatter name in v.name.
110
func (v *formatterValue) Set(value string) error {
111
if v.formatters[value] == nil {
112
return fmt.Errorf("unknown format %q", value)
118
// String returns the chosen formatter name.
119
func (v *formatterValue) String() string {
123
// doc returns documentation for the --format flag.
124
func (v *formatterValue) doc() string {
125
choices := make([]string, len(v.formatters))
127
for name := range v.formatters {
131
sort.Strings(choices)
132
return "Specify output format (" + strings.Join(choices, "|") + ")"
135
// format runs the chosen formatter on value.
136
func (v *formatterValue) format(value interface{}) ([]byte, error) {
137
return v.formatters[v.name](value)
140
// Output is responsible for interpreting output-related command line flags
141
// and writing a value to a file or to stdout as directed.
143
formatter *formatterValue
147
// AddFlags injects the --format and --output command line flags into f.
148
func (c *Output) AddFlags(f *gnuflag.FlagSet, defaultFormatter string, formatters map[string]Formatter) {
149
c.formatter = newFormatterValue(defaultFormatter, formatters)
150
f.Var(c.formatter, "format", c.formatter.doc())
151
f.StringVar(&c.outPath, "o", "", "Specify an output file")
152
f.StringVar(&c.outPath, "output", "", "")
155
// Write formats and outputs the value as directed by the --format and
156
// --output command line flags.
157
func (c *Output) Write(ctx *Context, value interface{}) (err error) {
162
path := ctx.AbsPath(c.outPath)
164
if f, err = os.Create(path); err != nil {
170
bytes, err := c.formatter.format(value)
175
_, err = target.Write(bytes)
177
_, err = target.Write([]byte{'\n'})
183
func (c *Output) Name() string {
184
return c.formatter.name