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