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 }