diff --git a/gitdiff/io.go b/gitdiff/io.go index 1c40655..bb3936b 100644 --- a/gitdiff/io.go +++ b/gitdiff/io.go @@ -1,84 +1,10 @@ package gitdiff import ( - "bufio" "errors" - "fmt" "io" ) -// StringReader is the interface that wraps the ReadString method. -// -// ReadString reads until the first occurrence of delim in the input, returning -// a string containing the data up to and including the delimiter. If -// ReadString encounters an error before finding a delimiter, it returns the -// data read before the error and the error itself (often io.EOF). ReadString -// returns err != nil if and only if the returned data does not end in delim. -type StringReader interface { - ReadString(delim byte) (string, error) -} - -type readStringReader interface { - io.Reader - StringReader -} - -// LineReader is the interface that wraps the ReadLine method. -// -// ReadLine reads the next full line in the input, returing the the data -// including the line ending character(s) and the zero-indexed line number. If -// ReadLine encounters an error before reaching the end of the line, it returns -// the data read before the error, the number of the line, and the error itself -// (often io.EOF). ReadLine returns err != nil if and only if the returned data -// is not a complete line. -// -// If an implementation defines other methods for reading the same input, line -// numbers may be incorrect if calls to ReadLine are mixed with calls to other -// read methods. -type LineReader interface { - ReadLine() (string, int64, error) -} - -// NewLineReader returns a LineReader starting at a specific line and using the -// newline character, \n, as a line separator. If r is a StringReader, it is -// used directly. Otherwise, it is wrapped in a way that may read extra data -// from the underlying input. -func NewLineReader(r io.Reader, lineno int64) LineReader { - sr, ok := r.(readStringReader) - if !ok { - sr = bufio.NewReader(r) - } - return &lineReader{r: sr, n: lineno} -} - -type lineReader struct { - r readStringReader - n int64 -} - -func (lr *lineReader) ReadLine() (line string, lineno int64, err error) { - lineno = lr.n - line, err = lr.r.ReadString('\n') - if err == nil { - lr.n++ - } - return -} - -// unwrapLineReader returns a plain io.Reader that was converted to a -// LineReader by wrapping or casting. It should only be called from functions -// that accept an io.Reader as an argument and then convert it. -func unwrapLineReader(lr LineReader) io.Reader { - switch r := lr.(type) { - case io.Reader: - return r - case *lineReader: - return r.r - default: - panic(fmt.Sprintf("%T does not implement io.Reader and is not a gitdiff wrapper", lr)) - } -} - // LineReaderAt is the interface that wraps the ReadLinesAt method. // // ReadLinesAt reads len(lines) into lines starting at line offset in the diff --git a/gitdiff/io_test.go b/gitdiff/io_test.go deleted file mode 100644 index c9c92da..0000000 --- a/gitdiff/io_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package gitdiff - -import ( - "io" - "strings" - "testing" -) - -func TestLineReader(t *testing.T) { - const content = "first line\nsecond line\nthird line\npartial fourth line" - - t.Run("readLine", func(t *testing.T) { - r := NewLineReader(strings.NewReader(content), 0) - - lines := []struct { - Data string - Err error - }{ - {"first line\n", nil}, - {"second line\n", nil}, - {"third line\n", nil}, - {"partial fourth line", io.EOF}, - } - - for i, line := range lines { - d, n, err := r.ReadLine() - if err != line.Err { - if line.Err == nil { - t.Fatalf("error reading line: %v", err) - } else { - t.Fatalf("expected %v while reading line, but got %v", line.Err, err) - } - } - if d != line.Data { - t.Errorf("incorrect line data: expected %q, actual %q", line.Data, d) - } - if n != int64(i) { - t.Errorf("incorrect line number: expected %d, actual %d", i, n) - } - } - }) - - t.Run("readLineOffset", func(t *testing.T) { - r := NewLineReader(strings.NewReader(content), 10) - - d, n, err := r.ReadLine() - if err != nil { - t.Fatalf("error reading line: %v", err) - } - if d != "first line\n" { - t.Errorf("incorrect line data: expected %q, actual %q", "first line\n", d) - } - if n != 10 { - t.Errorf("incorrect line number: expected %d, actual %d", 10, n) - } - }) -} diff --git a/gitdiff/parser.go b/gitdiff/parser.go index d0f0d5d..d44465a 100644 --- a/gitdiff/parser.go +++ b/gitdiff/parser.go @@ -4,6 +4,7 @@ package gitdiff import ( + "bufio" "fmt" "io" ) @@ -11,9 +12,6 @@ import ( // Parse parses a patch with changes to one or more files. Any content before // the first file is returned as the second value. If an error occurs while // parsing, it returns all files parsed before the error. -// -// If r is a LineReader or StringReader, it is used directly. Otherwise, it is -// wrapped in a way that may read extra data from the underlying input. func Parse(r io.Reader) ([]*File, string, error) { p := newParser(r) @@ -69,8 +67,12 @@ func Parse(r io.Reader) ([]*File, string, error) { // - if returning an object, advance to the first line after the object // - any exported parsing methods must initialize the parser by calling Next() +type stringReader interface { + ReadString(delim byte) (string, error) +} + type parser struct { - r LineReader + r stringReader eof bool lineno int64 @@ -78,10 +80,10 @@ type parser struct { } func newParser(r io.Reader) *parser { - if lr, ok := r.(LineReader); ok { - return &parser{r: lr} + if r, ok := r.(stringReader); ok { + return &parser{r: r} } - return &parser{r: NewLineReader(r, 0)} + return &parser{r: bufio.NewReader(r)} } // Next advances the parser by one line. It returns any error encountered while @@ -117,7 +119,7 @@ func (p *parser) shiftLines() (err error) { for i := 0; i < len(p.lines)-1; i++ { p.lines[i] = p.lines[i+1] } - p.lines[len(p.lines)-1], _, err = p.r.ReadLine() + p.lines[len(p.lines)-1], err = p.r.ReadString('\n') return }