...

Source file src/github.com/docker/distribution/registry/api/errcode/errors.go

Documentation: github.com/docker/distribution/registry/api/errcode

     1  package errcode
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"strings"
     7  )
     8  
     9  // ErrorCoder is the base interface for ErrorCode and Error allowing
    10  // users of each to just call ErrorCode to get the real ID of each
    11  type ErrorCoder interface {
    12  	ErrorCode() ErrorCode
    13  }
    14  
    15  // ErrorCode represents the error type. The errors are serialized via strings
    16  // and the integer format may change and should *never* be exported.
    17  type ErrorCode int
    18  
    19  var _ error = ErrorCode(0)
    20  
    21  // ErrorCode just returns itself
    22  func (ec ErrorCode) ErrorCode() ErrorCode {
    23  	return ec
    24  }
    25  
    26  // Error returns the ID/Value
    27  func (ec ErrorCode) Error() string {
    28  	// NOTE(stevvooe): Cannot use message here since it may have unpopulated args.
    29  	return strings.ToLower(strings.Replace(ec.String(), "_", " ", -1))
    30  }
    31  
    32  // Descriptor returns the descriptor for the error code.
    33  func (ec ErrorCode) Descriptor() ErrorDescriptor {
    34  	d, ok := errorCodeToDescriptors[ec]
    35  
    36  	if !ok {
    37  		return ErrorCodeUnknown.Descriptor()
    38  	}
    39  
    40  	return d
    41  }
    42  
    43  // String returns the canonical identifier for this error code.
    44  func (ec ErrorCode) String() string {
    45  	return ec.Descriptor().Value
    46  }
    47  
    48  // Message returned the human-readable error message for this error code.
    49  func (ec ErrorCode) Message() string {
    50  	return ec.Descriptor().Message
    51  }
    52  
    53  // MarshalText encodes the receiver into UTF-8-encoded text and returns the
    54  // result.
    55  func (ec ErrorCode) MarshalText() (text []byte, err error) {
    56  	return []byte(ec.String()), nil
    57  }
    58  
    59  // UnmarshalText decodes the form generated by MarshalText.
    60  func (ec *ErrorCode) UnmarshalText(text []byte) error {
    61  	desc, ok := idToDescriptors[string(text)]
    62  
    63  	if !ok {
    64  		desc = ErrorCodeUnknown.Descriptor()
    65  	}
    66  
    67  	*ec = desc.Code
    68  
    69  	return nil
    70  }
    71  
    72  // WithMessage creates a new Error struct based on the passed-in info and
    73  // overrides the Message property.
    74  func (ec ErrorCode) WithMessage(message string) Error {
    75  	return Error{
    76  		Code:    ec,
    77  		Message: message,
    78  	}
    79  }
    80  
    81  // WithDetail creates a new Error struct based on the passed-in info and
    82  // set the Detail property appropriately
    83  func (ec ErrorCode) WithDetail(detail interface{}) Error {
    84  	return Error{
    85  		Code:    ec,
    86  		Message: ec.Message(),
    87  	}.WithDetail(detail)
    88  }
    89  
    90  // WithArgs creates a new Error struct and sets the Args slice
    91  func (ec ErrorCode) WithArgs(args ...interface{}) Error {
    92  	return Error{
    93  		Code:    ec,
    94  		Message: ec.Message(),
    95  	}.WithArgs(args...)
    96  }
    97  
    98  // Error provides a wrapper around ErrorCode with extra Details provided.
    99  type Error struct {
   100  	Code    ErrorCode   `json:"code"`
   101  	Message string      `json:"message"`
   102  	Detail  interface{} `json:"detail,omitempty"`
   103  
   104  	// TODO(duglin): See if we need an "args" property so we can do the
   105  	// variable substitution right before showing the message to the user
   106  }
   107  
   108  var _ error = Error{}
   109  
   110  // ErrorCode returns the ID/Value of this Error
   111  func (e Error) ErrorCode() ErrorCode {
   112  	return e.Code
   113  }
   114  
   115  // Error returns a human readable representation of the error.
   116  func (e Error) Error() string {
   117  	return fmt.Sprintf("%s: %s", e.Code.Error(), e.Message)
   118  }
   119  
   120  // WithDetail will return a new Error, based on the current one, but with
   121  // some Detail info added
   122  func (e Error) WithDetail(detail interface{}) Error {
   123  	return Error{
   124  		Code:    e.Code,
   125  		Message: e.Message,
   126  		Detail:  detail,
   127  	}
   128  }
   129  
   130  // WithArgs uses the passed-in list of interface{} as the substitution
   131  // variables in the Error's Message string, but returns a new Error
   132  func (e Error) WithArgs(args ...interface{}) Error {
   133  	return Error{
   134  		Code:    e.Code,
   135  		Message: fmt.Sprintf(e.Code.Message(), args...),
   136  		Detail:  e.Detail,
   137  	}
   138  }
   139  
   140  // ErrorDescriptor provides relevant information about a given error code.
   141  type ErrorDescriptor struct {
   142  	// Code is the error code that this descriptor describes.
   143  	Code ErrorCode
   144  
   145  	// Value provides a unique, string key, often captilized with
   146  	// underscores, to identify the error code. This value is used as the
   147  	// keyed value when serializing api errors.
   148  	Value string
   149  
   150  	// Message is a short, human readable decription of the error condition
   151  	// included in API responses.
   152  	Message string
   153  
   154  	// Description provides a complete account of the errors purpose, suitable
   155  	// for use in documentation.
   156  	Description string
   157  
   158  	// HTTPStatusCode provides the http status code that is associated with
   159  	// this error condition.
   160  	HTTPStatusCode int
   161  }
   162  
   163  // ParseErrorCode returns the value by the string error code.
   164  // `ErrorCodeUnknown` will be returned if the error is not known.
   165  func ParseErrorCode(value string) ErrorCode {
   166  	ed, ok := idToDescriptors[value]
   167  	if ok {
   168  		return ed.Code
   169  	}
   170  
   171  	return ErrorCodeUnknown
   172  }
   173  
   174  // Errors provides the envelope for multiple errors and a few sugar methods
   175  // for use within the application.
   176  type Errors []error
   177  
   178  var _ error = Errors{}
   179  
   180  func (errs Errors) Error() string {
   181  	switch len(errs) {
   182  	case 0:
   183  		return "<nil>"
   184  	case 1:
   185  		return errs[0].Error()
   186  	default:
   187  		msg := "errors:\n"
   188  		for _, err := range errs {
   189  			msg += err.Error() + "\n"
   190  		}
   191  		return msg
   192  	}
   193  }
   194  
   195  // Len returns the current number of errors.
   196  func (errs Errors) Len() int {
   197  	return len(errs)
   198  }
   199  
   200  // MarshalJSON converts slice of error, ErrorCode or Error into a
   201  // slice of Error - then serializes
   202  func (errs Errors) MarshalJSON() ([]byte, error) {
   203  	var tmpErrs struct {
   204  		Errors []Error `json:"errors,omitempty"`
   205  	}
   206  
   207  	for _, daErr := range errs {
   208  		var err Error
   209  
   210  		switch daErr := daErr.(type) {
   211  		case ErrorCode:
   212  			err = daErr.WithDetail(nil)
   213  		case Error:
   214  			err = daErr
   215  		default:
   216  			err = ErrorCodeUnknown.WithDetail(daErr)
   217  
   218  		}
   219  
   220  		// If the Error struct was setup and they forgot to set the
   221  		// Message field (meaning its "") then grab it from the ErrCode
   222  		msg := err.Message
   223  		if msg == "" {
   224  			msg = err.Code.Message()
   225  		}
   226  
   227  		tmpErrs.Errors = append(tmpErrs.Errors, Error{
   228  			Code:    err.Code,
   229  			Message: msg,
   230  			Detail:  err.Detail,
   231  		})
   232  	}
   233  
   234  	return json.Marshal(tmpErrs)
   235  }
   236  
   237  // UnmarshalJSON deserializes []Error and then converts it into slice of
   238  // Error or ErrorCode
   239  func (errs *Errors) UnmarshalJSON(data []byte) error {
   240  	var tmpErrs struct {
   241  		Errors []Error
   242  	}
   243  
   244  	if err := json.Unmarshal(data, &tmpErrs); err != nil {
   245  		return err
   246  	}
   247  
   248  	var newErrs Errors
   249  	for _, daErr := range tmpErrs.Errors {
   250  		// If Message is empty or exactly matches the Code's message string
   251  		// then just use the Code, no need for a full Error struct
   252  		if daErr.Detail == nil && (daErr.Message == "" || daErr.Message == daErr.Code.Message()) {
   253  			// Error's w/o details get converted to ErrorCode
   254  			newErrs = append(newErrs, daErr.Code)
   255  		} else {
   256  			// Error's w/ details are untouched
   257  			newErrs = append(newErrs, Error{
   258  				Code:    daErr.Code,
   259  				Message: daErr.Message,
   260  				Detail:  daErr.Detail,
   261  			})
   262  		}
   263  	}
   264  
   265  	*errs = newErrs
   266  	return nil
   267  }
   268  

View as plain text