this repo has no description
0
fork

Configure Feed

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

Add LineReader and StringReader interfaces

LineReader is a wrapper around StringReader that counts line numbers.
This is not currently used by the parser, but will be used by the apply
functions.

+123 -10
+58
gitdiff/io.go
··· 1 + package gitdiff 2 + 3 + import ( 4 + "bufio" 5 + "io" 6 + ) 7 + 8 + // StringReader is the interface that wraps the ReadString method. 9 + type StringReader interface { 10 + // ReadString reads until the first occurrence of delim in the input, 11 + // returning a string containing the data up to and including the 12 + // delimiter. If ReadString encounters an error before finding a delimiter, 13 + // it returns the data read before the error and the error itself (often 14 + // io.EOF). ReadString returns err != nil if and only if the returned data 15 + // does not end in delim. 16 + ReadString(delim byte) (string, error) 17 + } 18 + 19 + // LineReader is the interface that wraps the ReadLine method. 20 + type LineReader interface { 21 + // ReadLine reads the next full line in the input, returing the the data 22 + // including the line ending character(s) and the zero-indexed line number. 23 + // If ReadLine encounters an error before reaching the end of the line, it 24 + // returns the data read before the error and the error itself (often 25 + // io.EOF). ReadLine returns err != nil if and only if the returned data is 26 + // not a complete line. 27 + // 28 + // If the implementation defines other methods for reading the same input, 29 + // line numbers may be incorrect if calls to ReadLine are mixed with calls 30 + // to other read methods. 31 + ReadLine() (string, int, error) 32 + } 33 + 34 + // NewLineReader returns a LineReader for a reader starting at a specific line 35 + // using the newline character, \n, as a line separator. If r is a 36 + // StringReader, it is used directly. Otherwise, it is wrapped in a way that 37 + // may read extra data from the underlying input. 38 + func NewLineReader(r io.Reader, lineno int) LineReader { 39 + sr, ok := r.(StringReader) 40 + if !ok { 41 + sr = bufio.NewReader(r) 42 + } 43 + return &lineReader{r: sr, n: lineno} 44 + } 45 + 46 + type lineReader struct { 47 + r StringReader 48 + n int 49 + } 50 + 51 + func (lr *lineReader) ReadLine() (line string, lineno int, err error) { 52 + lineno = lr.n 53 + line, err = lr.r.ReadString('\n') 54 + if err == nil { 55 + lr.n++ 56 + } 57 + return 58 + }
+57
gitdiff/io_test.go
··· 1 + package gitdiff 2 + 3 + import ( 4 + "io" 5 + "strings" 6 + "testing" 7 + ) 8 + 9 + func TestLineReader(t *testing.T) { 10 + const content = "first line\nsecond line\nthird line\npartial fourth line" 11 + 12 + t.Run("readLine", func(t *testing.T) { 13 + r := NewLineReader(strings.NewReader(content), 0) 14 + 15 + lines := []struct { 16 + Data string 17 + Err error 18 + }{ 19 + {"first line\n", nil}, 20 + {"second line\n", nil}, 21 + {"third line\n", nil}, 22 + {"partial fourth line", io.EOF}, 23 + } 24 + 25 + for i, line := range lines { 26 + d, n, err := r.ReadLine() 27 + if err != line.Err { 28 + if line.Err == nil { 29 + t.Fatalf("error reading line: %v", err) 30 + } else { 31 + t.Fatalf("expected %v while reading line, but got %v", line.Err, err) 32 + } 33 + } 34 + if d != line.Data { 35 + t.Errorf("incorrect line data: expected %q, actual %q", line.Data, d) 36 + } 37 + if n != i { 38 + t.Errorf("incorrect line number: expected %d, actual %d", i, n) 39 + } 40 + } 41 + }) 42 + 43 + t.Run("readLineOffset", func(t *testing.T) { 44 + r := NewLineReader(strings.NewReader(content), 10) 45 + 46 + d, n, err := r.ReadLine() 47 + if err != nil { 48 + t.Fatalf("error reading line: %v", err) 49 + } 50 + if d != "first line\n" { 51 + t.Errorf("incorrect line data: expected %q, actual %q", "first line\n", d) 52 + } 53 + if n != 10 { 54 + t.Errorf("incorrect line number: expected %d, actual %d", 10, n) 55 + } 56 + }) 57 + }
+8 -10
gitdiff/parser.go
··· 4 4 package gitdiff 5 5 6 6 import ( 7 - "bufio" 8 7 "fmt" 9 8 "io" 10 9 ) ··· 12 11 // Parse parses a patch with changes to one or more files. Any content before 13 12 // the first file is returned as the second value. If an error occurs while 14 13 // parsing, it returns all files parsed before the error. 14 + // 15 + // If r is a LineReader or StringReader, it is used directly. Otherwise, it is 16 + // wrapped in a way that may read extra data from the underlying input. 15 17 func Parse(r io.Reader) ([]*File, string, error) { 16 18 p := newParser(r) 17 19 ··· 67 69 // - if returning an object, advance to the first line after the object 68 70 // - any exported parsing methods must initialize the parser by calling Next() 69 71 70 - type stringReader interface { 71 - ReadString(delim byte) (string, error) 72 - } 73 - 74 72 type parser struct { 75 - r stringReader 73 + r LineReader 76 74 77 75 eof bool 78 76 lineno int64 ··· 80 78 } 81 79 82 80 func newParser(r io.Reader) *parser { 83 - if r, ok := r.(stringReader); ok { 84 - return &parser{r: r} 81 + if lr, ok := r.(LineReader); ok { 82 + return &parser{r: lr} 85 83 } 86 - return &parser{r: bufio.NewReader(r)} 84 + return &parser{r: NewLineReader(r, 0)} 87 85 } 88 86 89 87 // Next advances the parser by one line. It returns any error encountered while ··· 119 117 for i := 0; i < len(p.lines)-1; i++ { 120 118 p.lines[i] = p.lines[i+1] 121 119 } 122 - p.lines[len(p.lines)-1], err = p.r.ReadString('\n') 120 + p.lines[len(p.lines)-1], _, err = p.r.ReadLine() 123 121 return 124 122 } 125 123