github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/xerrors/issues.go (about) 1 package xerrors 2 3 import ( 4 "errors" 5 "strconv" 6 "strings" 7 8 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" 9 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Issue" 10 11 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring" 12 ) 13 14 type issues []*Ydb_Issue.IssueMessage 15 16 func (ii issues) String() string { 17 if len(ii) == 0 { 18 return "" 19 } 20 b := xstring.Buffer() 21 defer b.Free() 22 b.WriteByte('[') 23 for i, m := range ii { 24 if i != 0 { 25 b.WriteByte(',') 26 } 27 b.WriteByte('{') 28 if p := m.GetPosition(); p != nil { 29 if file := p.GetFile(); file != "" { 30 b.WriteString(file) 31 b.WriteByte(':') 32 } 33 b.WriteString(strconv.Itoa(int(p.GetRow()))) 34 b.WriteByte(':') 35 b.WriteString(strconv.Itoa(int(p.GetColumn()))) 36 b.WriteString(" => ") 37 } 38 if code := m.GetIssueCode(); code != 0 { 39 b.WriteByte('#') 40 b.WriteString(strconv.Itoa(int(code))) 41 b.WriteByte(' ') 42 } 43 b.WriteByte('\'') 44 b.WriteString(strings.TrimSuffix(m.GetMessage(), ".")) 45 b.WriteByte('\'') 46 if len(m.GetIssues()) > 0 { 47 b.WriteByte(' ') 48 b.WriteString(issues(m.GetIssues()).String()) 49 } 50 b.WriteByte('}') 51 } 52 b.WriteByte(']') 53 54 return b.String() 55 } 56 57 // NewWithIssues returns error which contains child issues 58 func NewWithIssues(text string, issues ...error) error { 59 err := &withIssuesError{ 60 reason: text, 61 } 62 for i := range issues { 63 if issues[i] != nil { 64 err.issues = append(err.issues, issues[i]) 65 } 66 } 67 68 return err 69 } 70 71 type withIssuesError struct { 72 reason string 73 issues []error 74 } 75 76 func (e *withIssuesError) isYdbError() {} 77 78 func (e *withIssuesError) Error() string { 79 b := xstring.Buffer() 80 defer b.Free() 81 if len(e.reason) > 0 { 82 b.WriteString(e.reason) 83 b.WriteString(", issues: [") 84 } else { 85 b.WriteString("multiple errors: [") 86 } 87 for i, issue := range e.issues { 88 if i != 0 { 89 b.WriteString(", ") 90 } 91 b.WriteString(issue.Error()) 92 } 93 b.WriteString("]") 94 95 return b.String() 96 } 97 98 func (e *withIssuesError) As(target interface{}) bool { 99 for _, err := range e.issues { 100 if As(err, target) { 101 return true 102 } 103 } 104 105 return false 106 } 107 108 func (e *withIssuesError) Is(target error) bool { 109 for _, err := range e.issues { 110 if Is(err, target) { 111 return true 112 } 113 } 114 115 return false 116 } 117 118 func (e *withIssuesError) Unwrap() []error { 119 return e.issues 120 } 121 122 // Issue struct 123 type Issue struct { 124 Message string 125 Code uint32 126 Severity uint32 127 } 128 129 type IssueIterator []*Ydb_Issue.IssueMessage 130 131 func (it IssueIterator) Len() int { 132 return len(it) 133 } 134 135 func (it IssueIterator) Get(i int) (issue Issue, nested IssueIterator) { 136 x := it[i] 137 if xs := x.GetIssues(); len(xs) > 0 { 138 nested = IssueIterator(xs) 139 } 140 141 return Issue{ 142 Message: x.GetMessage(), 143 Code: x.GetIssueCode(), 144 Severity: x.GetSeverity(), 145 }, nested 146 } 147 148 func IterateByIssues(err error, it func(message string, code Ydb.StatusIds_StatusCode, severity uint32)) { 149 var o *operationError 150 if !errors.As(err, &o) { 151 return 152 } 153 iterate(o.Issues(), it) 154 } 155 156 func iterate( 157 issues []*Ydb_Issue.IssueMessage, 158 it func(message string, code Ydb.StatusIds_StatusCode, severity uint32), 159 ) { 160 for _, issue := range issues { 161 it(issue.GetMessage(), Ydb.StatusIds_StatusCode(issue.GetIssueCode()), issue.GetSeverity()) 162 iterate(issue.GetIssues(), it) 163 } 164 }