github.com/vvnotw/moby@v1.13.1/pkg/streamformatter/streamformatter.go (about) 1 // Package streamformatter provides helper functions to format a stream. 2 package streamformatter 3 4 import ( 5 "encoding/json" 6 "fmt" 7 "io" 8 9 "github.com/docker/docker/pkg/jsonmessage" 10 "github.com/docker/docker/pkg/progress" 11 ) 12 13 // StreamFormatter formats a stream, optionally using JSON. 14 type StreamFormatter struct { 15 json bool 16 } 17 18 // NewStreamFormatter returns a simple StreamFormatter 19 func NewStreamFormatter() *StreamFormatter { 20 return &StreamFormatter{} 21 } 22 23 // NewJSONStreamFormatter returns a StreamFormatter configured to stream json 24 func NewJSONStreamFormatter() *StreamFormatter { 25 return &StreamFormatter{true} 26 } 27 28 const streamNewline = "\r\n" 29 30 var streamNewlineBytes = []byte(streamNewline) 31 32 // FormatStream formats the specified stream. 33 func (sf *StreamFormatter) FormatStream(str string) []byte { 34 if sf.json { 35 b, err := json.Marshal(&jsonmessage.JSONMessage{Stream: str}) 36 if err != nil { 37 return sf.FormatError(err) 38 } 39 return append(b, streamNewlineBytes...) 40 } 41 return []byte(str + "\r") 42 } 43 44 // FormatStatus formats the specified objects according to the specified format (and id). 45 func (sf *StreamFormatter) FormatStatus(id, format string, a ...interface{}) []byte { 46 str := fmt.Sprintf(format, a...) 47 if sf.json { 48 b, err := json.Marshal(&jsonmessage.JSONMessage{ID: id, Status: str}) 49 if err != nil { 50 return sf.FormatError(err) 51 } 52 return append(b, streamNewlineBytes...) 53 } 54 return []byte(str + streamNewline) 55 } 56 57 // FormatError formats the specified error. 58 func (sf *StreamFormatter) FormatError(err error) []byte { 59 if sf.json { 60 jsonError, ok := err.(*jsonmessage.JSONError) 61 if !ok { 62 jsonError = &jsonmessage.JSONError{Message: err.Error()} 63 } 64 if b, err := json.Marshal(&jsonmessage.JSONMessage{Error: jsonError, ErrorMessage: err.Error()}); err == nil { 65 return append(b, streamNewlineBytes...) 66 } 67 return []byte("{\"error\":\"format error\"}" + streamNewline) 68 } 69 return []byte("Error: " + err.Error() + streamNewline) 70 } 71 72 // FormatProgress formats the progress information for a specified action. 73 func (sf *StreamFormatter) FormatProgress(id, action string, progress *jsonmessage.JSONProgress, aux interface{}) []byte { 74 if progress == nil { 75 progress = &jsonmessage.JSONProgress{} 76 } 77 if sf.json { 78 var auxJSON *json.RawMessage 79 if aux != nil { 80 auxJSONBytes, err := json.Marshal(aux) 81 if err != nil { 82 return nil 83 } 84 auxJSON = new(json.RawMessage) 85 *auxJSON = auxJSONBytes 86 } 87 b, err := json.Marshal(&jsonmessage.JSONMessage{ 88 Status: action, 89 ProgressMessage: progress.String(), 90 Progress: progress, 91 ID: id, 92 Aux: auxJSON, 93 }) 94 if err != nil { 95 return nil 96 } 97 return append(b, streamNewlineBytes...) 98 } 99 endl := "\r" 100 if progress.String() == "" { 101 endl += "\n" 102 } 103 return []byte(action + " " + progress.String() + endl) 104 } 105 106 // NewProgressOutput returns a progress.Output object that can be passed to 107 // progress.NewProgressReader. 108 func (sf *StreamFormatter) NewProgressOutput(out io.Writer, newLines bool) progress.Output { 109 return &progressOutput{ 110 sf: sf, 111 out: out, 112 newLines: newLines, 113 } 114 } 115 116 type progressOutput struct { 117 sf *StreamFormatter 118 out io.Writer 119 newLines bool 120 } 121 122 // WriteProgress formats progress information from a ProgressReader. 123 func (out *progressOutput) WriteProgress(prog progress.Progress) error { 124 var formatted []byte 125 if prog.Message != "" { 126 formatted = out.sf.FormatStatus(prog.ID, prog.Message) 127 } else { 128 jsonProgress := jsonmessage.JSONProgress{Current: prog.Current, Total: prog.Total} 129 formatted = out.sf.FormatProgress(prog.ID, prog.Action, &jsonProgress, prog.Aux) 130 } 131 _, err := out.out.Write(formatted) 132 if err != nil { 133 return err 134 } 135 136 if out.newLines && prog.LastUpdate { 137 _, err = out.out.Write(out.sf.FormatStatus("", "")) 138 return err 139 } 140 141 return nil 142 } 143 144 // StdoutFormatter is a streamFormatter that writes to the standard output. 145 type StdoutFormatter struct { 146 io.Writer 147 *StreamFormatter 148 } 149 150 func (sf *StdoutFormatter) Write(buf []byte) (int, error) { 151 formattedBuf := sf.StreamFormatter.FormatStream(string(buf)) 152 n, err := sf.Writer.Write(formattedBuf) 153 if n != len(formattedBuf) { 154 return n, io.ErrShortWrite 155 } 156 return len(buf), err 157 } 158 159 // StderrFormatter is a streamFormatter that writes to the standard error. 160 type StderrFormatter struct { 161 io.Writer 162 *StreamFormatter 163 } 164 165 func (sf *StderrFormatter) Write(buf []byte) (int, error) { 166 formattedBuf := sf.StreamFormatter.FormatStream("\033[91m" + string(buf) + "\033[0m") 167 n, err := sf.Writer.Write(formattedBuf) 168 if n != len(formattedBuf) { 169 return n, io.ErrShortWrite 170 } 171 return len(buf), err 172 }