diff options
Diffstat (limited to 'cmd/podman/formats')
-rw-r--r-- | cmd/podman/formats/formats.go | 143 | ||||
-rw-r--r-- | cmd/podman/formats/templates.go | 78 |
2 files changed, 221 insertions, 0 deletions
diff --git a/cmd/podman/formats/formats.go b/cmd/podman/formats/formats.go new file mode 100644 index 000000000..4b6527b30 --- /dev/null +++ b/cmd/podman/formats/formats.go @@ -0,0 +1,143 @@ +package formats + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "strings" + "text/tabwriter" + "text/template" + + "github.com/ghodss/yaml" + "github.com/pkg/errors" +) + +const ( + // JSONString const to save on duplicate variable names + JSONString = "json" + // IDString const to save on duplicates for Go templates + IDString = "{{.ID}}" +) + +// Writer interface for outputs +type Writer interface { + Out() error +} + +// JSONStructArray for JSON output +type JSONStructArray struct { + Output []interface{} +} + +// StdoutTemplateArray for Go template output +type StdoutTemplateArray struct { + Output []interface{} + Template string + Fields map[string]string +} + +// JSONStruct for JSON output +type JSONStruct struct { + Output interface{} +} + +// StdoutTemplate for Go template output +type StdoutTemplate struct { + Output interface{} + Template string + Fields map[string]string +} + +// YAMLStruct for YAML output +type YAMLStruct struct { + Output interface{} +} + +// Out method for JSON Arrays +func (j JSONStructArray) Out() error { + data, err := json.MarshalIndent(j.Output, "", " ") + if err != nil { + return err + } + + // JSON returns a byte array with a literal null [110 117 108 108] in it + // if it is passed empty data. We used bytes.Compare to see if that is + // the case. + if diff := bytes.Compare(data, []byte("null")); diff == 0 { + data = []byte("[]") + } + + // If the we did get NULL back, we should spit out {} which is + // at least valid JSON for the consumer. + fmt.Printf("%s\n", data) + return nil +} + +// Out method for Go templates +func (t StdoutTemplateArray) Out() error { + w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) + if strings.HasPrefix(t.Template, "table") { + // replace any spaces with tabs in template so that tabwriter can align it + t.Template = strings.Replace(strings.TrimSpace(t.Template[5:]), " ", "\t", -1) + headerTmpl, err := template.New("header").Funcs(headerFunctions).Parse(t.Template) + if err != nil { + return errors.Wrapf(err, "Template parsing error") + } + err = headerTmpl.Execute(w, t.Fields) + if err != nil { + return err + } + fmt.Fprintln(w, "") + } + t.Template = strings.Replace(t.Template, " ", "\t", -1) + tmpl, err := template.New("image").Funcs(basicFunctions).Parse(t.Template) + if err != nil { + return errors.Wrapf(err, "Template parsing error") + } + for _, img := range t.Output { + basicTmpl := tmpl.Funcs(basicFunctions) + err = basicTmpl.Execute(w, img) + if err != nil { + return err + } + fmt.Fprintln(w, "") + } + return w.Flush() +} + +// Out method for JSON struct +func (j JSONStruct) Out() error { + data, err := json.MarshalIndent(j.Output, "", " ") + if err != nil { + return err + } + fmt.Printf("%s\n", data) + return nil +} + +//Out method for Go templates +func (t StdoutTemplate) Out() error { + tmpl, err := template.New("image").Parse(t.Template) + if err != nil { + return errors.Wrapf(err, "template parsing error") + } + err = tmpl.Execute(os.Stdout, t.Output) + if err != nil { + return err + } + fmt.Println() + return nil +} + +// Out method for YAML +func (y YAMLStruct) Out() error { + var buf []byte + var err error + buf, err = yaml.Marshal(y.Output) + if err != nil { + return err + } + fmt.Println(string(buf)) + return nil +} diff --git a/cmd/podman/formats/templates.go b/cmd/podman/formats/templates.go new file mode 100644 index 000000000..c2582552a --- /dev/null +++ b/cmd/podman/formats/templates.go @@ -0,0 +1,78 @@ +package formats + +import ( + "bytes" + "encoding/json" + "strings" + "text/template" +) + +// basicFunctions are the set of initial +// functions provided to every template. +var basicFunctions = template.FuncMap{ + "json": func(v interface{}) string { + buf := &bytes.Buffer{} + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + _ = enc.Encode(v) + // Remove the trailing new line added by the encoder + return strings.TrimSpace(buf.String()) + }, + "split": strings.Split, + "join": strings.Join, + "title": strings.Title, + "lower": strings.ToLower, + "upper": strings.ToUpper, + "pad": padWithSpace, + "truncate": truncateWithLength, +} + +// HeaderFunctions are used to created headers of a table. +// This is a replacement of basicFunctions for header generation +// because we want the header to remain intact. +// Some functions like `split` are irrelevant so not added. +var headerFunctions = template.FuncMap{ + "json": func(v string) string { + return v + }, + "title": func(v string) string { + return v + }, + "lower": func(v string) string { + return v + }, + "upper": func(v string) string { + return v + }, + "truncate": func(v string, l int) string { + return v + }, +} + +// Parse creates a new anonymous template with the basic functions +// and parses the given format. +func Parse(format string) (*template.Template, error) { + return NewParse("", format) +} + +// NewParse creates a new tagged template with the basic functions +// and parses the given format. +func NewParse(tag, format string) (*template.Template, error) { + return template.New(tag).Funcs(basicFunctions).Parse(format) +} + +// padWithSpace adds whitespace to the input if the input is non-empty +func padWithSpace(source string, prefix, suffix int) string { + if source == "" { + return source + } + return strings.Repeat(" ", prefix) + source + strings.Repeat(" ", suffix) +} + +// truncateWithLength truncates the source string up to the length provided by the input +func truncateWithLength(source string, length int) string { + if len(source) < length { + return source + } + return source[:length] +} |