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.
···11+package gitdiff
22+33+import (
44+ "bufio"
55+ "io"
66+)
77+88+// StringReader is the interface that wraps the ReadString method.
99+type StringReader interface {
1010+ // ReadString reads until the first occurrence of delim in the input,
1111+ // returning a string containing the data up to and including the
1212+ // delimiter. If ReadString encounters an error before finding a delimiter,
1313+ // it returns the data read before the error and the error itself (often
1414+ // io.EOF). ReadString returns err != nil if and only if the returned data
1515+ // does not end in delim.
1616+ ReadString(delim byte) (string, error)
1717+}
1818+1919+// LineReader is the interface that wraps the ReadLine method.
2020+type LineReader interface {
2121+ // ReadLine reads the next full line in the input, returing the the data
2222+ // including the line ending character(s) and the zero-indexed line number.
2323+ // If ReadLine encounters an error before reaching the end of the line, it
2424+ // returns the data read before the error and the error itself (often
2525+ // io.EOF). ReadLine returns err != nil if and only if the returned data is
2626+ // not a complete line.
2727+ //
2828+ // If the implementation defines other methods for reading the same input,
2929+ // line numbers may be incorrect if calls to ReadLine are mixed with calls
3030+ // to other read methods.
3131+ ReadLine() (string, int, error)
3232+}
3333+3434+// NewLineReader returns a LineReader for a reader starting at a specific line
3535+// using the newline character, \n, as a line separator. If r is a
3636+// StringReader, it is used directly. Otherwise, it is wrapped in a way that
3737+// may read extra data from the underlying input.
3838+func NewLineReader(r io.Reader, lineno int) LineReader {
3939+ sr, ok := r.(StringReader)
4040+ if !ok {
4141+ sr = bufio.NewReader(r)
4242+ }
4343+ return &lineReader{r: sr, n: lineno}
4444+}
4545+4646+type lineReader struct {
4747+ r StringReader
4848+ n int
4949+}
5050+5151+func (lr *lineReader) ReadLine() (line string, lineno int, err error) {
5252+ lineno = lr.n
5353+ line, err = lr.r.ReadString('\n')
5454+ if err == nil {
5555+ lr.n++
5656+ }
5757+ return
5858+}
+57
gitdiff/io_test.go
···11+package gitdiff
22+33+import (
44+ "io"
55+ "strings"
66+ "testing"
77+)
88+99+func TestLineReader(t *testing.T) {
1010+ const content = "first line\nsecond line\nthird line\npartial fourth line"
1111+1212+ t.Run("readLine", func(t *testing.T) {
1313+ r := NewLineReader(strings.NewReader(content), 0)
1414+1515+ lines := []struct {
1616+ Data string
1717+ Err error
1818+ }{
1919+ {"first line\n", nil},
2020+ {"second line\n", nil},
2121+ {"third line\n", nil},
2222+ {"partial fourth line", io.EOF},
2323+ }
2424+2525+ for i, line := range lines {
2626+ d, n, err := r.ReadLine()
2727+ if err != line.Err {
2828+ if line.Err == nil {
2929+ t.Fatalf("error reading line: %v", err)
3030+ } else {
3131+ t.Fatalf("expected %v while reading line, but got %v", line.Err, err)
3232+ }
3333+ }
3434+ if d != line.Data {
3535+ t.Errorf("incorrect line data: expected %q, actual %q", line.Data, d)
3636+ }
3737+ if n != i {
3838+ t.Errorf("incorrect line number: expected %d, actual %d", i, n)
3939+ }
4040+ }
4141+ })
4242+4343+ t.Run("readLineOffset", func(t *testing.T) {
4444+ r := NewLineReader(strings.NewReader(content), 10)
4545+4646+ d, n, err := r.ReadLine()
4747+ if err != nil {
4848+ t.Fatalf("error reading line: %v", err)
4949+ }
5050+ if d != "first line\n" {
5151+ t.Errorf("incorrect line data: expected %q, actual %q", "first line\n", d)
5252+ }
5353+ if n != 10 {
5454+ t.Errorf("incorrect line number: expected %d, actual %d", 10, n)
5555+ }
5656+ })
5757+}
+8-10
gitdiff/parser.go
···44package gitdiff
5566import (
77- "bufio"
87 "fmt"
98 "io"
109)
···1211// Parse parses a patch with changes to one or more files. Any content before
1312// the first file is returned as the second value. If an error occurs while
1413// parsing, it returns all files parsed before the error.
1414+//
1515+// If r is a LineReader or StringReader, it is used directly. Otherwise, it is
1616+// wrapped in a way that may read extra data from the underlying input.
1517func Parse(r io.Reader) ([]*File, string, error) {
1618 p := newParser(r)
1719···6769// - if returning an object, advance to the first line after the object
6870// - any exported parsing methods must initialize the parser by calling Next()
69717070-type stringReader interface {
7171- ReadString(delim byte) (string, error)
7272-}
7373-7472type parser struct {
7575- r stringReader
7373+ r LineReader
76747775 eof bool
7876 lineno int64
···8078}
81798280func newParser(r io.Reader) *parser {
8383- if r, ok := r.(stringReader); ok {
8484- return &parser{r: r}
8181+ if lr, ok := r.(LineReader); ok {
8282+ return &parser{r: lr}
8583 }
8686- return &parser{r: bufio.NewReader(r)}
8484+ return &parser{r: NewLineReader(r, 0)}
8785}
88868987// Next advances the parser by one line. It returns any error encountered while
···119117 for i := 0; i < len(p.lines)-1; i++ {
120118 p.lines[i] = p.lines[i+1]
121119 }
122122- p.lines[len(p.lines)-1], err = p.r.ReadString('\n')
120120+ p.lines[len(p.lines)-1], _, err = p.r.ReadLine()
123121 return
124122}
125123