github.com/alloyzeus/go-azfl@v0.0.0-20231220071816-9740126a2d07/errors/named.go (about) 1 package errors 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 type NamedError interface { 9 Unwrappable 10 11 Name() string 12 Value() any 13 } 14 15 type NamedErrorBuilder interface { 16 NamedError 17 18 // Desc returns a copy with descriptor is set to desc. 19 Desc(desc ErrorDescriptor) NamedErrorBuilder 20 21 // DescMsg sets the descriptor with the provided string. For the best 22 // experience, descMsg should be defined as a constant so that the error 23 // users could use it to identify an error. For non-constant descriptor 24 // use the Wrap method. 25 DescMsg(descMsg string) NamedErrorBuilder 26 27 // Hint provides a clue for the developers on how to fix the error. 28 Hint(hintText string) NamedErrorBuilder 29 30 Fieldset(fields ...NamedError) NamedErrorBuilder 31 32 // Rewrap collects descriptor, wrapped, and fields from err and include 33 // them into the new error. 34 Rewrap(err error) NamedErrorBuilder 35 36 // Value provides the value associated to the name. 37 Val(value any) NamedErrorBuilder 38 39 // Wrap returns a copy with wrapped error is set to detailingError. 40 Wrap(detailingError error) NamedErrorBuilder 41 } 42 43 func N(name string) NamedErrorBuilder { 44 return &namedError{name: name} 45 } 46 47 // NamedValueMalformed creates an NamedValue with name is set to 48 // the value of valueName and descriptor is set to ErrValueMalformed. 49 func NamedValueMalformed(valueName string) NamedErrorBuilder { 50 return &namedError{ 51 name: valueName, 52 descriptor: ErrValueMalformed, 53 } 54 } 55 56 // NamedValueUnsupported creates an NamedValue with name is set to 57 // the value of valueName and descriptor is set to ErrValueUnsupported. 58 func NamedValueUnsupported(valueName string) NamedErrorBuilder { 59 return &namedError{ 60 name: valueName, 61 descriptor: ErrValueUnsupported, 62 } 63 } 64 65 type namedError struct { 66 name string 67 descriptor ErrorDescriptor 68 hintText string 69 fields []NamedError 70 value any 71 wrapped error 72 } 73 74 func (e *namedError) Error() string { 75 suffix := namedSetToString(e.fields) 76 if suffix != "" { 77 suffix = ": " + suffix 78 } 79 if e.hintText != "" { 80 suffix = suffix + ". " + e.hintText 81 } 82 var descStr string 83 if e.descriptor != nil { 84 descStr = e.descriptor.Error() 85 } 86 causeStr := errorString(e.wrapped) 87 if causeStr == "" { 88 causeStr = descStr 89 } else if descStr != "" { 90 causeStr = descStr + ": " + causeStr 91 } 92 93 var name string 94 if e.name != "" { 95 if e.value != nil { 96 valStr := fmt.Sprintf("%v", e.value) 97 if strings.ContainsAny(valStr, string([]byte{'\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0})) { 98 name = fmt.Sprintf("%s=%q", e.name, valStr) 99 } else { 100 name = fmt.Sprintf("%s=%s", e.name, valStr) 101 } 102 } else { 103 name = e.name 104 } 105 } 106 107 if name != "" { 108 if causeStr != "" { 109 return name + ": " + causeStr + suffix 110 } 111 return name + suffix 112 } 113 if causeStr != "" { 114 return causeStr + suffix 115 } 116 if suffix != "" { 117 return strings.TrimPrefix(suffix, ": ") 118 } 119 return "" 120 } 121 122 func (e namedError) Unwrap() error { return e.wrapped } 123 func (e namedError) Name() string { return e.name } 124 func (e namedError) Value() any { return e.value } 125 func (e namedError) Descriptor() ErrorDescriptor { 126 if e.descriptor != nil { 127 return e.descriptor 128 } 129 if desc, ok := e.wrapped.(ErrorDescriptor); ok { 130 return desc 131 } 132 return nil 133 } 134 func (e namedError) FieldErrors() []NamedError { 135 return copyNamedSet(e.fields) 136 } 137 138 func (e namedError) Desc(desc ErrorDescriptor) NamedErrorBuilder { 139 e.descriptor = desc 140 return &e 141 } 142 143 func (e namedError) DescMsg(descMsg string) NamedErrorBuilder { 144 e.descriptor = constantErrorDescriptor(descMsg) 145 return &e 146 } 147 148 func (e namedError) Hint(hintText string) NamedErrorBuilder { 149 e.hintText = hintText 150 return &e 151 } 152 153 func (e namedError) Fieldset(fields ...NamedError) NamedErrorBuilder { 154 e.fields = copyNamedSet(fields) 155 return &e 156 } 157 158 func (e namedError) Rewrap(err error) NamedErrorBuilder { 159 if err != nil { 160 if descErr, _ := err.(ErrorDescriptor); descErr != nil { 161 e.descriptor = descErr 162 e.value = nil 163 e.wrapped = nil 164 e.fields = nil 165 } else { 166 e.descriptor = UnwrapDescriptor(err) 167 e.value = unwrapValue(err) 168 e.wrapped = Unwrap(err) 169 e.fields = UnwrapFieldErrors(err) 170 } 171 } 172 return &e 173 } 174 175 func (e namedError) Val(value any) NamedErrorBuilder { 176 e.value = value 177 return &e 178 } 179 180 func (e namedError) Wrap(detailingError error) NamedErrorBuilder { 181 e.wrapped = detailingError 182 return &e 183 } 184 185 func copyNamedSet(namedSet []NamedError) []NamedError { 186 var copiedFields []NamedError 187 if len(namedSet) > 0 { 188 copiedFields = make([]NamedError, 0, len(namedSet)) 189 for _, e := range namedSet { 190 if e != nil { 191 copiedFields = append(copiedFields, e) 192 } 193 } 194 } 195 return copiedFields 196 } 197 198 func namedSetToString(namedSet []NamedError) string { 199 if flen := len(namedSet); flen > 0 { 200 parts := make([]string, 0, flen) 201 for _, sub := range namedSet { 202 parts = append(parts, sub.Error()) 203 } 204 return strings.Join(parts, ", ") 205 } 206 return "" 207 } 208 209 func unwrapValue(err error) any { 210 if err != nil { 211 if d, ok := err.(valueHolder); ok { 212 return d.Value() 213 } 214 } 215 return nil 216 } 217 218 type valueHolder interface { 219 Value() any 220 }