this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cue/errors: add package

Change-Id: I2f5383e775282a8325201ab08c599f2dfb650857

+447
+255
cue/errors/errors.go
··· 1 + // Copyright 2018 The CUE Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // http://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + // Package errors defines shared types for handling CUE errors. 16 + package errors // import "cuelang.org/go/cue/errors" 17 + 18 + import ( 19 + "io" 20 + "sort" 21 + 22 + "cuelang.org/go/cue/token" 23 + "golang.org/x/exp/errors" 24 + "golang.org/x/exp/errors/fmt" 25 + ) 26 + 27 + // New is a convenience wrapper for errors.New in the core library. 28 + func New(msg string) error { 29 + return errors.New(msg) 30 + } 31 + 32 + // A Handler is a generic error handler used throughout CUE packages. 33 + // 34 + // The position points to the beginning of the offending value. 35 + type Handler func(pos token.Position, msg string) 36 + 37 + // Error is the common error message. 38 + type Error interface { 39 + Position() token.Position 40 + 41 + // Error reports the error message without position information. 42 + Error() string 43 + } 44 + 45 + // // TODO: make Error an interface that returns a list of positions. 46 + 47 + // In an List, an error is represented by an *posError. 48 + // The position Pos, if valid, points to the beginning of 49 + // the offending token, and the error condition is described 50 + // by Msg. 51 + type posError struct { 52 + pos token.Position 53 + msg string 54 + 55 + // The underlying error that triggered this one, if any. 56 + err error 57 + } 58 + 59 + // E creates a new error. 60 + func E(args ...interface{}) error { 61 + e := &posError{} 62 + update(e, args) 63 + return e 64 + } 65 + 66 + // Augment adorns an existing error with new information. 67 + func Augment(err error, args ...interface{}) error { 68 + e, ok := err.(*posError) 69 + if !ok { 70 + e = &posError{err: err} 71 + } 72 + update(e, args) 73 + return e 74 + } 75 + 76 + func update(e *posError, args []interface{}) { 77 + err := e.err 78 + for _, a := range args { 79 + switch x := a.(type) { 80 + case string: 81 + e.msg = x 82 + case token.Position: 83 + e.pos = x 84 + case []token.Position: 85 + // TODO: do something more clever 86 + if len(x) > 0 { 87 + e.pos = x[0] 88 + } 89 + case *posError: 90 + copy := *x 91 + err = &copy 92 + e.err = combine(e.err, err) 93 + case error: 94 + e.err = combine(e.err, x) 95 + } 96 + } 97 + } 98 + 99 + func combine(a, b error) error { 100 + switch x := a.(type) { 101 + case nil: 102 + return b 103 + case List: 104 + x.add(toErr(b)) 105 + return x 106 + default: 107 + return List{toErr(a), toErr(b)} 108 + } 109 + } 110 + 111 + func toErr(err error) Error { 112 + if e, ok := err.(Error); ok { 113 + return e 114 + } 115 + return &posError{err: err} 116 + } 117 + 118 + func (e posError) Position() token.Position { 119 + return e.pos 120 + } 121 + 122 + // Error implements the error interface. 123 + func (e posError) Error() string { return fmt.Sprint(e) } 124 + 125 + func (e posError) Format(p errors.Printer) error { 126 + next := e.err 127 + if e.msg == "" { 128 + next = errFormat(p, e.err) 129 + } else { 130 + p.Print(e.msg) 131 + } 132 + if p.Detail() && e.pos.Filename != "" || e.pos.IsValid() { 133 + p.Printf("%s", e.pos.String()) 134 + } 135 + return next 136 + 137 + } 138 + 139 + func errFormat(p errors.Printer, err error) (next error) { 140 + switch v := err.(type) { 141 + case errors.Formatter: 142 + err = v.Format(p) 143 + default: 144 + p.Print(err) 145 + err = nil 146 + } 147 + 148 + return err 149 + } 150 + 151 + // List is a list of *posError. 152 + // The zero value for an List is an empty List ready to use. 153 + // 154 + type List []Error 155 + 156 + func (p *List) add(err Error) { 157 + *p = append(*p, err) 158 + } 159 + 160 + // AddNew adds an Error with given position and error message to an List. 161 + func (p *List) AddNew(pos token.Position, msg string) { 162 + p.add(&posError{pos: pos, msg: msg}) 163 + } 164 + 165 + // Add adds an Error with given position and error message to an List. 166 + func (p *List) Add(err error) { 167 + p.add(toErr(err)) 168 + } 169 + 170 + // Reset resets an List to no errors. 171 + func (p *List) Reset() { *p = (*p)[0:0] } 172 + 173 + // List implements the sort Interface. 174 + func (p List) Len() int { return len(p) } 175 + func (p List) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 176 + 177 + func (p List) Less(i, j int) bool { 178 + e := p[i].Position() 179 + f := p[j].Position() 180 + // Note that it is not sufficient to simply compare file offsets because 181 + // the offsets do not reflect modified line information (through //line 182 + // comments). 183 + if e.Filename != f.Filename { 184 + return e.Filename < f.Filename 185 + } 186 + if e.Line != f.Line { 187 + return e.Line < f.Line 188 + } 189 + if e.Column != f.Column { 190 + return e.Column < f.Column 191 + } 192 + return p[i].Error() < p[j].Error() 193 + } 194 + 195 + // Sort sorts an List. *posError entries are sorted by position, 196 + // other errors are sorted by error message, and before any *posError 197 + // entry. 198 + // 199 + func (p List) Sort() { 200 + sort.Sort(p) 201 + } 202 + 203 + // RemoveMultiples sorts an List and removes all but the first error per line. 204 + func (p *List) RemoveMultiples() { 205 + sort.Sort(p) 206 + var last token.Position // initial last.Line is != any legal error line 207 + i := 0 208 + for _, e := range *p { 209 + pos := e.Position() 210 + if pos.Filename != last.Filename || pos.Line != last.Line { 211 + last = pos 212 + (*p)[i] = e 213 + i++ 214 + } 215 + } 216 + (*p) = (*p)[0:i] 217 + } 218 + 219 + // An List implements the error interface. 220 + func (p List) Error() string { 221 + switch len(p) { 222 + case 0: 223 + return "no errors" 224 + case 1: 225 + return p[0].Error() 226 + } 227 + return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1) 228 + } 229 + 230 + // Err returns an error equivalent to this error list. 231 + // If the list is empty, Err returns nil. 232 + func (p List) Err() error { 233 + if len(p) == 0 { 234 + return nil 235 + } 236 + return p 237 + } 238 + 239 + // Print is a utility function that prints a list of errors to w, 240 + // one error per line, if the err parameter is an List. Otherwise 241 + // it prints the err string. 242 + // 243 + func Print(w io.Writer, err error) { 244 + if list, ok := err.(List); ok { 245 + for _, e := range list { 246 + printError(w, e) 247 + } 248 + } else if err != nil { 249 + printError(w, toErr(err)) 250 + } 251 + } 252 + 253 + func printError(w io.Writer, err Error) { 254 + fmt.Fprintf(w, "%+v\n", err) 255 + }
+192
cue/errors/errors_test.go
··· 1 + // Copyright 2018 The CUE Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // http://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + package errors 16 + 17 + import ( 18 + "bytes" 19 + "testing" 20 + 21 + "cuelang.org/go/cue/token" 22 + ) 23 + 24 + func TestError_Error(t *testing.T) { 25 + tests := []struct { 26 + name string 27 + e Error 28 + want string 29 + }{ 30 + // TODO: Add test cases. 31 + } 32 + for _, tt := range tests { 33 + if got := tt.e.Error(); got != tt.want { 34 + t.Errorf("%q. Error.Error() = %v, want %v", tt.name, got, tt.want) 35 + } 36 + } 37 + } 38 + 39 + func TestErrorList_Add(t *testing.T) { 40 + type args struct { 41 + pos token.Position 42 + msg string 43 + } 44 + tests := []struct { 45 + name string 46 + p *List 47 + args args 48 + }{ 49 + // TODO: Add test cases. 50 + } 51 + for _, tt := range tests { 52 + tt.p.AddNew(tt.args.pos, tt.args.msg) 53 + } 54 + } 55 + 56 + func TestErrorList_Reset(t *testing.T) { 57 + tests := []struct { 58 + name string 59 + p *List 60 + }{ 61 + // TODO: Add test cases. 62 + } 63 + for _, tt := range tests { 64 + tt.p.Reset() 65 + } 66 + } 67 + 68 + func TestErrorList_Len(t *testing.T) { 69 + tests := []struct { 70 + name string 71 + p List 72 + want int 73 + }{ 74 + // TODO: Add test cases. 75 + } 76 + for _, tt := range tests { 77 + if got := tt.p.Len(); got != tt.want { 78 + t.Errorf("%q. List.Len() = %v, want %v", tt.name, got, tt.want) 79 + } 80 + } 81 + } 82 + 83 + func TestErrorList_Swap(t *testing.T) { 84 + type args struct { 85 + i int 86 + j int 87 + } 88 + tests := []struct { 89 + name string 90 + p List 91 + args args 92 + }{ 93 + // TODO: Add test cases. 94 + } 95 + for _, tt := range tests { 96 + tt.p.Swap(tt.args.i, tt.args.j) 97 + } 98 + } 99 + 100 + func TestErrorList_Less(t *testing.T) { 101 + type args struct { 102 + i int 103 + j int 104 + } 105 + tests := []struct { 106 + name string 107 + p List 108 + args args 109 + want bool 110 + }{ 111 + // TODO: Add test cases. 112 + } 113 + for _, tt := range tests { 114 + if got := tt.p.Less(tt.args.i, tt.args.j); got != tt.want { 115 + t.Errorf("%q. List.Less() = %v, want %v", tt.name, got, tt.want) 116 + } 117 + } 118 + } 119 + 120 + func TestErrorList_Sort(t *testing.T) { 121 + tests := []struct { 122 + name string 123 + p List 124 + }{ 125 + // TODO: Add test cases. 126 + } 127 + for _, tt := range tests { 128 + tt.p.Sort() 129 + } 130 + } 131 + 132 + func TestErrorList_RemoveMultiples(t *testing.T) { 133 + tests := []struct { 134 + name string 135 + p *List 136 + }{ 137 + // TODO: Add test cases. 138 + } 139 + for _, tt := range tests { 140 + tt.p.RemoveMultiples() 141 + } 142 + } 143 + 144 + func TestErrorList_Error(t *testing.T) { 145 + tests := []struct { 146 + name string 147 + p List 148 + want string 149 + }{ 150 + // TODO: Add test cases. 151 + } 152 + for _, tt := range tests { 153 + if got := tt.p.Error(); got != tt.want { 154 + t.Errorf("%q. List.Error() = %v, want %v", tt.name, got, tt.want) 155 + } 156 + } 157 + } 158 + 159 + func TestErrorList_Err(t *testing.T) { 160 + tests := []struct { 161 + name string 162 + p List 163 + wantErr bool 164 + }{ 165 + // TODO: Add test cases. 166 + } 167 + for _, tt := range tests { 168 + if err := tt.p.Err(); (err != nil) != tt.wantErr { 169 + t.Errorf("%q. List.Err() error = %v, wantErr %v", tt.name, err, tt.wantErr) 170 + } 171 + } 172 + } 173 + 174 + func TestPrintError(t *testing.T) { 175 + type args struct { 176 + err error 177 + } 178 + tests := []struct { 179 + name string 180 + args args 181 + wantW string 182 + }{ 183 + // TODO: Add test cases. 184 + } 185 + for _, tt := range tests { 186 + w := &bytes.Buffer{} 187 + Print(w, tt.args.err) 188 + if gotW := w.String(); gotW != tt.wantW { 189 + t.Errorf("%q. PrintError() = %v, want %v", tt.name, gotW, tt.wantW) 190 + } 191 + } 192 + }