this repo has no description
0
fork

Configure Feed

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

Improve conflict error detection

Conflict errors are now represented by an exported type that is
compatible with errors.Is instead of using the method defined on
ApplyError. This makes the tests slightly cleaner and should be more
idiomatic for clients.

+39 -24
+27 -13
gitdiff/apply.go
··· 5 5 "io" 6 6 ) 7 7 8 - type conflictError string 8 + // Conflict indicates an apply failed due to a conflict between the patch and 9 + // the source content. 10 + // 11 + // Users can test if an error was caused by a conflict by using errors.Is with 12 + // an empty Conflict: 13 + // 14 + // if errors.Is(err, &Conflict{}) { 15 + // // handle conflict 16 + // } 17 + // 18 + type Conflict struct { 19 + msg string 20 + } 9 21 10 - func (e conflictError) Error() string { 11 - return "conflict: " + string(e) 22 + func (c *Conflict) Error() string { 23 + return "conflict: " + c.msg 24 + } 25 + 26 + // Is implements error matching for Conflict. Passing an empty instance of 27 + // Conflict always returns true. 28 + func (c *Conflict) Is(other error) bool { 29 + if other, ok := other.(*Conflict); ok { 30 + return other.msg == "" || other.msg == c.msg 31 + } 32 + return false 12 33 } 13 34 14 35 // ApplyError wraps an error that occurs during patch application with ··· 27 48 // Unwrap returns the wrapped error. 28 49 func (e *ApplyError) Unwrap() error { 29 50 return e.err 30 - } 31 - 32 - // Conflict returns true if the error is due to a conflict between the fragment 33 - // and the source data. 34 - func (e *ApplyError) Conflict() bool { 35 - _, ok := e.err.(conflictError) 36 - return ok 37 51 } 38 52 39 53 func (e *ApplyError) Error() string { ··· 152 166 switch line.Op { 153 167 case OpContext, OpDelete: 154 168 if src != line.Line { 155 - return conflictError("fragment line does not match src line") 169 + return &Conflict{"fragment line does not match src line"} 156 170 } 157 171 } 158 172 switch line.Op { ··· 176 190 return line, n, err 177 191 case n > limit: 178 192 if limit < 0 { 179 - return "", n, conflictError("cannot create new file from non-empty src") 193 + return "", n, &Conflict{"cannot create new file from non-empty src"} 180 194 } 181 - return "", n, conflictError("fragment overlaps with an applied fragment") 195 + return "", n, &Conflict{"fragment overlaps with an applied fragment"} 182 196 case err != nil: 183 197 if err == io.EOF { 184 198 err = io.ErrUnexpectedEOF
+12 -11
gitdiff/apply_test.go
··· 2 2 3 3 import ( 4 4 "bytes" 5 + "errors" 6 + "io" 5 7 "io/ioutil" 6 8 "path/filepath" 7 - "strings" 8 9 "testing" 9 10 ) 10 11 ··· 15 16 PatchFile string 16 17 DstFile string 17 18 18 - Err string 19 + Err error 19 20 }{ 20 21 "createFile": {File: "text_fragment_new"}, 21 22 "deleteFile": {File: "text_fragment_delete_all"}, ··· 34 35 "errorShortSrcBefore": { 35 36 SrcFile: "text_fragment_error", 36 37 PatchFile: "text_fragment_error_short_src_before", 37 - Err: "unexpected EOF", 38 + Err: io.ErrUnexpectedEOF, 38 39 }, 39 40 "errorShortSrc": { 40 41 SrcFile: "text_fragment_error", 41 42 PatchFile: "text_fragment_error_short_src", 42 - Err: "unexpected EOF", 43 + Err: io.ErrUnexpectedEOF, 43 44 }, 44 45 "errorContextConflict": { 45 46 SrcFile: "text_fragment_error", 46 47 PatchFile: "text_fragment_error_context_conflict", 47 - Err: "conflict", 48 + Err: &Conflict{}, 48 49 }, 49 50 "errorDeleteConflict": { 50 51 SrcFile: "text_fragment_error", 51 52 PatchFile: "text_fragment_error_delete_conflict", 52 - Err: "conflict", 53 + Err: &Conflict{}, 53 54 }, 54 55 "errorNewFile": { 55 56 SrcFile: "text_fragment_error", 56 57 PatchFile: "text_fragment_error_new_file", 57 - Err: "conflict", 58 + Err: &Conflict{}, 58 59 }, 59 60 } 60 61 ··· 75 76 patch := loadFile(test.PatchFile, test.File, "patch") 76 77 77 78 var result []byte 78 - if test.Err == "" { 79 + if test.Err == nil { 79 80 result = loadFile(test.DstFile, test.File, "dst") 80 81 } 81 82 ··· 88 89 89 90 var dst bytes.Buffer 90 91 err = frag.ApplyStrict(&dst, NewLineReader(bytes.NewReader(src), 0)) 91 - if test.Err != "" { 92 + if test.Err != nil { 92 93 if err == nil { 93 94 t.Fatalf("expected error applying fragment, but got nil") 94 95 } 95 - if !strings.Contains(err.Error(), test.Err) { 96 - t.Fatalf("incorrect apply error: expected %q, actual %q", test.Err, err.Error()) 96 + if !errors.Is(err, test.Err) { 97 + t.Fatalf("incorrect apply error: expected: %T (%v), actual: %T (%v)", test.Err, test.Err, err, err) 97 98 } 98 99 return 99 100 }