github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/cli/command/formatter/formatter.go (about) 1 package formatter 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "strings" 8 "text/tabwriter" 9 "text/template" 10 11 "github.com/docker/docker/utils/templates" 12 ) 13 14 // Format keys used to specify certain kinds of output formats 15 const ( 16 TableFormatKey = "table" 17 RawFormatKey = "raw" 18 PrettyFormatKey = "pretty" 19 20 defaultQuietFormat = "{{.ID}}" 21 ) 22 23 // Format is the format string rendered using the Context 24 type Format string 25 26 // IsTable returns true if the format is a table-type format 27 func (f Format) IsTable() bool { 28 return strings.HasPrefix(string(f), TableFormatKey) 29 } 30 31 // Contains returns true if the format contains the substring 32 func (f Format) Contains(sub string) bool { 33 return strings.Contains(string(f), sub) 34 } 35 36 // Context contains information required by the formatter to print the output as desired. 37 type Context struct { 38 // Output is the output stream to which the formatted string is written. 39 Output io.Writer 40 // Format is used to choose raw, table or custom format for the output. 41 Format Format 42 // Trunc when set to true will truncate the output of certain fields such as Container ID. 43 Trunc bool 44 45 // internal element 46 finalFormat string 47 header string 48 buffer *bytes.Buffer 49 } 50 51 func (c *Context) preFormat() { 52 c.finalFormat = string(c.Format) 53 54 // TODO: handle this in the Format type 55 if c.Format.IsTable() { 56 c.finalFormat = c.finalFormat[len(TableFormatKey):] 57 } 58 59 c.finalFormat = strings.Trim(c.finalFormat, " ") 60 r := strings.NewReplacer(`\t`, "\t", `\n`, "\n") 61 c.finalFormat = r.Replace(c.finalFormat) 62 } 63 64 func (c *Context) parseFormat() (*template.Template, error) { 65 tmpl, err := templates.Parse(c.finalFormat) 66 if err != nil { 67 return tmpl, fmt.Errorf("Template parsing error: %v\n", err) 68 } 69 return tmpl, err 70 } 71 72 func (c *Context) postFormat(tmpl *template.Template, subContext subContext) { 73 if c.Format.IsTable() { 74 if len(c.header) == 0 { 75 // if we still don't have a header, we didn't have any containers so we need to fake it to get the right headers from the template 76 tmpl.Execute(bytes.NewBufferString(""), subContext) 77 c.header = subContext.FullHeader() 78 } 79 80 t := tabwriter.NewWriter(c.Output, 20, 1, 3, ' ', 0) 81 t.Write([]byte(c.header)) 82 t.Write([]byte("\n")) 83 c.buffer.WriteTo(t) 84 t.Flush() 85 } else { 86 c.buffer.WriteTo(c.Output) 87 } 88 } 89 90 func (c *Context) contextFormat(tmpl *template.Template, subContext subContext) error { 91 if err := tmpl.Execute(c.buffer, subContext); err != nil { 92 return fmt.Errorf("Template parsing error: %v\n", err) 93 } 94 if c.Format.IsTable() && len(c.header) == 0 { 95 c.header = subContext.FullHeader() 96 } 97 c.buffer.WriteString("\n") 98 return nil 99 } 100 101 // SubFormat is a function type accepted by Write() 102 type SubFormat func(func(subContext) error) error 103 104 // Write the template to the buffer using this Context 105 func (c *Context) Write(sub subContext, f SubFormat) error { 106 c.buffer = bytes.NewBufferString("") 107 c.preFormat() 108 109 tmpl, err := c.parseFormat() 110 if err != nil { 111 return err 112 } 113 114 subFormat := func(subContext subContext) error { 115 return c.contextFormat(tmpl, subContext) 116 } 117 if err := f(subFormat); err != nil { 118 return err 119 } 120 121 c.postFormat(tmpl, sub) 122 return nil 123 }