github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/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) []byte {
    74  	if progress == nil {
    75  		progress = &jsonmessage.JSONProgress{}
    76  	}
    77  	if sf.json {
    78  		b, err := json.Marshal(&jsonmessage.JSONMessage{
    79  			Status:          action,
    80  			ProgressMessage: progress.String(),
    81  			Progress:        progress,
    82  			ID:              id,
    83  		})
    84  		if err != nil {
    85  			return nil
    86  		}
    87  		return append(b, streamNewlineBytes...)
    88  	}
    89  	endl := "\r"
    90  	if progress.String() == "" {
    91  		endl += "\n"
    92  	}
    93  	return []byte(action + " " + progress.String() + endl)
    94  }
    95  
    96  // NewProgressOutput returns a progress.Output object that can be passed to
    97  // progress.NewProgressReader.
    98  func (sf *StreamFormatter) NewProgressOutput(out io.Writer, newLines bool) progress.Output {
    99  	return &progressOutput{
   100  		sf:       sf,
   101  		out:      out,
   102  		newLines: newLines,
   103  	}
   104  }
   105  
   106  type progressOutput struct {
   107  	sf       *StreamFormatter
   108  	out      io.Writer
   109  	newLines bool
   110  }
   111  
   112  // WriteProgress formats progress information from a ProgressReader.
   113  func (out *progressOutput) WriteProgress(prog progress.Progress) error {
   114  	var formatted []byte
   115  	if prog.Message != "" {
   116  		formatted = out.sf.FormatStatus(prog.ID, prog.Message)
   117  	} else {
   118  		jsonProgress := jsonmessage.JSONProgress{Current: prog.Current, Total: prog.Total}
   119  		formatted = out.sf.FormatProgress(prog.ID, prog.Action, &jsonProgress)
   120  	}
   121  	_, err := out.out.Write(formatted)
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	if out.newLines && prog.LastUpdate {
   127  		_, err = out.out.Write(out.sf.FormatStatus("", ""))
   128  		return err
   129  	}
   130  
   131  	return nil
   132  }
   133  
   134  // StdoutFormatter is a streamFormatter that writes to the standard output.
   135  type StdoutFormatter struct {
   136  	io.Writer
   137  	*StreamFormatter
   138  }
   139  
   140  func (sf *StdoutFormatter) Write(buf []byte) (int, error) {
   141  	formattedBuf := sf.StreamFormatter.FormatStream(string(buf))
   142  	n, err := sf.Writer.Write(formattedBuf)
   143  	if n != len(formattedBuf) {
   144  		return n, io.ErrShortWrite
   145  	}
   146  	return len(buf), err
   147  }
   148  
   149  // StderrFormatter is a streamFormatter that writes to the standard error.
   150  type StderrFormatter struct {
   151  	io.Writer
   152  	*StreamFormatter
   153  }
   154  
   155  func (sf *StderrFormatter) Write(buf []byte) (int, error) {
   156  	formattedBuf := sf.StreamFormatter.FormatStream("\033[91m" + string(buf) + "\033[0m")
   157  	n, err := sf.Writer.Write(formattedBuf)
   158  	if n != len(formattedBuf) {
   159  		return n, io.ErrShortWrite
   160  	}
   161  	return len(buf), err
   162  }