github.com/akerouanton/docker@v1.11.0-rc3/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  }