github.com/alloyzeus/go-azfl@v0.0.0-20231220071816-9740126a2d07/errors/entity.go (about)

     1  package errors
     2  
     3  import "strings"
     4  
     5  // EntityError is a class of errors describing errors in entities.
     6  //
     7  // An entity here is defined as anything that has identifier associated to it.
     8  // For example, a web page is an entity with its URL is the identifier.
     9  type EntityError interface {
    10  	Unwrappable
    11  	EntityIdentifier() string
    12  }
    13  
    14  func IsEntityError(err error) bool {
    15  	_, ok := err.(EntityError)
    16  	return ok
    17  }
    18  
    19  // Ent creates an instance of error that conforms EntityError. It takes
    20  // entityIdentifier which could be the name, key or URL of an entity. The
    21  // entityIdentifier should describe the 'what' while err describes the 'why'.
    22  //
    23  //   // Describes that the file ".config.yaml" does not exist.
    24  //   errors.Ent("./config.yaml", os.ErrNotExist)
    25  //
    26  //   // Describes that the site "https://example.com" is unreachable.
    27  //   errors.Ent("https://example.com/", errors.Msg("unreachable"))
    28  //
    29  func Ent(entityIdentifier string, err error) EntityError {
    30  	return &entityError{
    31  		identifier: entityIdentifier,
    32  		err:        err,
    33  	}
    34  }
    35  
    36  // EntFields is used to create an error that describes multiple field errors
    37  // of an entity.
    38  func EntFields(entityIdentifier string, fields ...EntityError) EntityError {
    39  	if fields != nil {
    40  		fields = fields[:]
    41  	}
    42  	return &entityError{
    43  		identifier: entityIdentifier,
    44  		fields:     fields,
    45  	}
    46  }
    47  
    48  // EntMsg creates an instance of error from an entitity identifier and the
    49  // error message which describes why the entity is considered error.
    50  func EntMsg(entityIdentifier string, errMsg string) EntityError {
    51  	return &entityError{
    52  		identifier: entityIdentifier,
    53  		err:        Msg(errMsg),
    54  	}
    55  }
    56  
    57  // EntInvalid creates an EntityError with err set to DataErrInvalid.
    58  func EntInvalid(entityIdentifier string, details error) EntityError {
    59  	return &entityError{
    60  		identifier: entityIdentifier,
    61  		err:        DescWrap(ErrValueInvalid, details),
    62  	}
    63  }
    64  
    65  func IsEntInvalidError(err error) bool {
    66  	if !IsEntityError(err) {
    67  		return false
    68  	}
    69  	if desc := UnwrapDescriptor(err); desc != nil {
    70  		return desc == ErrValueInvalid
    71  	}
    72  	return false
    73  }
    74  
    75  const (
    76  	// ErrEntityNotFound is used when the entity with the specified name
    77  	// cannot be found in the system.
    78  	ErrEntityNotFound = valueConstantErrorDescriptor("not found")
    79  
    80  	// ErrEntityConflict is used when the process of creating a new entity
    81  	// fails because an entity with the same name already exists.
    82  	ErrEntityConflict = valueConstantErrorDescriptor("conflict")
    83  
    84  	// ErrEntityUnreachable is used to describe that the system is unable
    85  	// to reach the system responsible of the specified entity.
    86  	ErrEntityUnreachable = valueConstantErrorDescriptor("unreachable")
    87  )
    88  
    89  func EntNotFound(entityIdentifier string, details error) EntityError {
    90  	return &entityError{
    91  		identifier: entityIdentifier,
    92  		err:        DescWrap(ErrEntityNotFound, details),
    93  	}
    94  }
    95  
    96  func IsEntNotFoundError(err error) bool {
    97  	if !IsEntityError(err) {
    98  		return false
    99  	}
   100  	if desc := UnwrapDescriptor(err); desc != nil {
   101  		return desc == ErrEntityNotFound
   102  	}
   103  	return false
   104  }
   105  
   106  type entityError struct {
   107  	identifier string
   108  	err        error
   109  	fields     []EntityError
   110  }
   111  
   112  var (
   113  	_ error         = &entityError{}
   114  	_ Unwrappable   = &entityError{}
   115  	_ EntityError   = &entityError{}
   116  	_ hasDescriptor = &entityError{}
   117  )
   118  
   119  func (e *entityError) Error() string {
   120  	suffix := e.fieldErrorsAsString()
   121  	if suffix != "" {
   122  		suffix = ": " + suffix
   123  	}
   124  	detailsStr := errorString(e.err)
   125  
   126  	if e.identifier != "" {
   127  		if detailsStr != "" {
   128  			return e.identifier + ": " + detailsStr + suffix
   129  		}
   130  		return e.identifier + suffix
   131  	}
   132  	if detailsStr != "" {
   133  		return "entity " + detailsStr + suffix
   134  	}
   135  	if suffix != "" {
   136  		return "entity" + suffix
   137  	}
   138  	return "entity error"
   139  }
   140  
   141  func (e entityError) fieldErrorsAsString() string {
   142  	if flen := len(e.fields); flen > 0 {
   143  		parts := make([]string, 0, flen)
   144  		for _, sub := range e.fields {
   145  			parts = append(parts, sub.Error())
   146  		}
   147  		return strings.Join(parts, ", ")
   148  	}
   149  	return ""
   150  }
   151  
   152  func (e *entityError) Unwrap() error            { return e.err }
   153  func (e *entityError) EntityIdentifier() string { return e.identifier }
   154  func (e *entityError) Descriptor() ErrorDescriptor {
   155  	if e == nil {
   156  		return nil
   157  	}
   158  	if desc, ok := e.err.(ErrorDescriptor); ok {
   159  		return desc
   160  	}
   161  	if desc := UnwrapDescriptor(e.err); desc != nil {
   162  		return desc
   163  	}
   164  	return nil
   165  }
   166  
   167  // EntityErrorSet is an interface to combine multiple EntityError instances
   168  // into a single error.
   169  type EntityErrorSet interface {
   170  	ErrorSet
   171  	EntityErrors() []EntityError
   172  }
   173  
   174  // UnwrapEntityErrorSet returns the contained EntityError instances if err is
   175  // indeed a EntityErrorSet.
   176  func UnwrapEntityErrorSet(err error) []EntityError {
   177  	if errSet := asEntityErrorSet(err); errSet != nil {
   178  		return errSet.EntityErrors()
   179  	}
   180  	return nil
   181  }
   182  
   183  // asEntityErrorSet returns err as an EntityErrorSet if err is indeed an
   184  // EntityErrorSet, otherwise it returns nil.
   185  func asEntityErrorSet(err error) EntityErrorSet {
   186  	e, _ := err.(EntityErrorSet)
   187  	return e
   188  }
   189  
   190  // EntSet creates a compound error comprised of multiple instances of
   191  // EntityError. Note that the resulting error is not an EntityError because
   192  // it has no identity.
   193  func EntSet(entityErrors ...EntityError) EntityErrorSet {
   194  	ours := make([]EntityError, len(entityErrors))
   195  	copy(ours, entityErrors)
   196  	return entErrorSet(ours)
   197  }
   198  
   199  type entErrorSet []EntityError
   200  
   201  var (
   202  	_ error          = entErrorSet{}
   203  	_ ErrorSet       = entErrorSet{}
   204  	_ EntityErrorSet = entErrorSet{}
   205  )
   206  
   207  func (e entErrorSet) Error() string {
   208  	if len(e) > 0 {
   209  		errs := make([]string, 0, len(e))
   210  		for _, ce := range e {
   211  			errs = append(errs, ce.Error())
   212  		}
   213  		s := strings.Join(errs, ", ")
   214  		if s != "" {
   215  			return s
   216  		}
   217  	}
   218  	return ""
   219  }
   220  
   221  func (e entErrorSet) Errors() []error {
   222  	if len(e) > 0 {
   223  		errs := make([]error, 0, len(e))
   224  		for _, i := range e {
   225  			errs = append(errs, i)
   226  		}
   227  		return errs
   228  	}
   229  	return nil
   230  }
   231  
   232  func (e entErrorSet) EntityErrors() []EntityError {
   233  	if len(e) > 0 {
   234  		errs := make([]EntityError, len(e))
   235  		copy(errs, e)
   236  		return errs
   237  	}
   238  	return nil
   239  }
   240  
   241  func copyFieldSet(fields []EntityError) []EntityError {
   242  	var copiedFields []EntityError
   243  	if len(fields) > 0 {
   244  		copiedFields = make([]EntityError, 0, len(fields))
   245  		for _, e := range fields {
   246  			if e != nil {
   247  				copiedFields = append(copiedFields, e)
   248  			}
   249  		}
   250  	}
   251  	return copiedFields
   252  }