1 // Copyright (C) MongoDB, Inc. 2023-present. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from: 8 // - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/awserr/types.go 9 // See THIRD-PARTY-NOTICES for original license terms 10 11 package awserr 12 13 import ( 14 "fmt" 15 ) 16 17 // SprintError returns a string of the formatted error code. 18 // 19 // Both extra and origErr are optional. If they are included their lines 20 // will be added, but if they are not included their lines will be ignored. 21 func SprintError(code, message, extra string, origErr error) string { 22 msg := fmt.Sprintf("%s: %s", code, message) 23 if extra != "" { 24 msg = fmt.Sprintf("%s\n\t%s", msg, extra) 25 } 26 if origErr != nil { 27 msg = fmt.Sprintf("%s\ncaused by: %s", msg, origErr.Error()) 28 } 29 return msg 30 } 31 32 // A baseError wraps the code and message which defines an error. It also 33 // can be used to wrap an original error object. 34 // 35 // Should be used as the root for errors satisfying the awserr.Error. Also 36 // for any error which does not fit into a specific error wrapper type. 37 type baseError struct { 38 // Classification of error 39 code string 40 41 // Detailed information about error 42 message string 43 44 // Optional original error this error is based off of. Allows building 45 // chained errors. 46 errs []error 47 } 48 49 // newBaseError returns an error object for the code, message, and errors. 50 // 51 // code is a short no whitespace phrase depicting the classification of 52 // the error that is being created. 53 // 54 // message is the free flow string containing detailed information about the 55 // error. 56 // 57 // origErrs is the error objects which will be nested under the new errors to 58 // be returned. 59 func newBaseError(code, message string, origErrs []error) *baseError { 60 b := &baseError{ 61 code: code, 62 message: message, 63 errs: origErrs, 64 } 65 66 return b 67 } 68 69 // Error returns the string representation of the error. 70 // 71 // See ErrorWithExtra for formatting. 72 // 73 // Satisfies the error interface. 74 func (b baseError) Error() string { 75 size := len(b.errs) 76 if size > 0 { 77 return SprintError(b.code, b.message, "", errorList(b.errs)) 78 } 79 80 return SprintError(b.code, b.message, "", nil) 81 } 82 83 // String returns the string representation of the error. 84 // Alias for Error to satisfy the stringer interface. 85 func (b baseError) String() string { 86 return b.Error() 87 } 88 89 // Code returns the short phrase depicting the classification of the error. 90 func (b baseError) Code() string { 91 return b.code 92 } 93 94 // Message returns the error details message. 95 func (b baseError) Message() string { 96 return b.message 97 } 98 99 // OrigErr returns the original error if one was set. Nil is returned if no 100 // error was set. This only returns the first element in the list. If the full 101 // list is needed, use BatchedErrors. 102 func (b baseError) OrigErr() error { 103 switch len(b.errs) { 104 case 0: 105 return nil 106 case 1: 107 return b.errs[0] 108 default: 109 if err, ok := b.errs[0].(Error); ok { 110 return NewBatchError(err.Code(), err.Message(), b.errs[1:]) 111 } 112 return NewBatchError("BatchedErrors", 113 "multiple errors occurred", b.errs) 114 } 115 } 116 117 // OrigErrs returns the original errors if one was set. An empty slice is 118 // returned if no error was set. 119 func (b baseError) OrigErrs() []error { 120 return b.errs 121 } 122 123 // An error list that satisfies the golang interface 124 type errorList []error 125 126 // Error returns the string representation of the error. 127 // 128 // Satisfies the error interface. 129 func (e errorList) Error() string { 130 msg := "" 131 // How do we want to handle the array size being zero 132 if size := len(e); size > 0 { 133 for i := 0; i < size; i++ { 134 msg += e[i].Error() 135 // We check the next index to see if it is within the slice. 136 // If it is, then we append a newline. We do this, because unit tests 137 // could be broken with the additional '\n' 138 if i+1 < size { 139 msg += "\n" 140 } 141 } 142 } 143 return msg 144 } 145