Skip to content

Commit

Permalink
allow callback break and return error (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wing924 authored Jun 23, 2019
1 parent fe8817f commit ce19dd1
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 7 deletions.
17 changes: 13 additions & 4 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ var (
ErrInvalidLabel = xerrors.New("invalid label")
// ErrInvalidValue is an error to describe value contains invalid char (ex. 'my_label:my_value\n')
ErrInvalidValue = xerrors.New("invalid value")
// Break is an error for break loop
Break = xerrors.New("break")
)

// ParseField parse LTSV-encoded field and return the label and value.
Expand Down Expand Up @@ -75,7 +77,7 @@ func (p Parser) ParseField(field []byte) (label []byte, value []byte, err error)

// ParseLine parse one line of LTSV-encoded data and call callback.
// The callback function will be called for each field.
func (p Parser) ParseLine(line []byte, callback func(label []byte, value []byte)) error {
func (p Parser) ParseLine(line []byte, callback func(label []byte, value []byte) error) error {
oriLine := line
for len(line) > 0 {
idx := bytes.IndexByte(line, p.FieldDelimiter)
Expand All @@ -95,7 +97,12 @@ func (p Parser) ParseLine(line []byte, callback func(label []byte, value []byte)
return xerrors.Errorf("bad line syntax %q: %w", string(oriLine), err)
}

callback(label, value)
if err = callback(label, value); err != nil {
if err == Break {
break
}
return xerrors.Errorf("ParseLine callback error: %w", err)
}
}
return nil
}
Expand All @@ -106,8 +113,9 @@ func (p Parser) ParseLineAsMap(line []byte, record map[string]string) (map[strin
if record == nil {
record = map[string]string{}
}
err := p.ParseLine(line, func(label []byte, value []byte) {
err := p.ParseLine(line, func(label []byte, value []byte) error {
record[string(label)] = string(value)
return nil
})
if err != nil {
return nil, xerrors.Errorf(": %w", err)
Expand All @@ -119,8 +127,9 @@ func (p Parser) ParseLineAsMap(line []byte, record map[string]string) (map[strin
// For reducing memory allocation, you can pass a slice to record to reuse the given slice.
func (p Parser) ParseLineAsSlice(line []byte, record []Field) ([]Field, error) {
record = record[:0]
err := p.ParseLine(line, func(label []byte, value []byte) {
err := p.ParseLine(line, func(label []byte, value []byte) error {
record = append(record, Field{string(label), string(value)})
return nil
})
if err != nil {
return nil, xerrors.Errorf(": %w", err)
Expand Down
40 changes: 38 additions & 2 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

func TestParseLine(t *testing.T) {
line := []byte("foo:123\tbar:456")
ParseLine(line, func(label []byte, value []byte) {
ParseLine(line, func(label []byte, value []byte) error {
val := string(value)
switch string(label) {
case "foo":
Expand All @@ -22,9 +22,34 @@ func TestParseLine(t *testing.T) {
default:
t.Errorf("unknown label: %s", string(label))
}
return nil
})
}

func TestParseLine_break(t *testing.T) {
line := []byte("foo:123\tbar:456")
counter := 0
err := ParseLine(line, func(label []byte, value []byte) error {
counter++
return Break
})
assert.NoError(t, err)
assert.Equal(t, 1, counter)
}

func TestParseLine_error(t *testing.T) {
line := []byte("foo:123\tbar:456")
counter := 0
customErr := xerrors.New("custom error")
err := ParseLine(line, func(label []byte, value []byte) error {
counter++
return customErr
})
assert.Error(t, err)
assert.True(t, xerrors.Is(err, customErr))
assert.Equal(t, 1, counter)
}

func TestParseLineAsMap(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -136,7 +161,7 @@ func TestParseField(t *testing.T) {
}
}

func BenchmarkParseLine(b *testing.B) {
func BenchmarkParseLineAsMap(b *testing.B) {
parser := DefaultParser
parser.StrictMode = false
line := []byte("host:127.0.0.1\tident:-\tuser:frank\ttime:[10/Oct/2000:13:55:36 -0700]\treq:GET /apache_pb.gif HTTP/1.0\tstatus:200\tsize:2326\treferer:http://www.example.com/start.html\tua:Mozilla/4.08 [en] (Win98; I ;Nav)")
Expand All @@ -145,3 +170,14 @@ func BenchmarkParseLine(b *testing.B) {
parser.ParseLineAsMap(line, m)
}
}

func BenchmarkParseLine(b *testing.B) {
parser := DefaultParser
parser.StrictMode = false
line := []byte("host:127.0.0.1\tident:-\tuser:frank\ttime:[10/Oct/2000:13:55:36 -0700]\treq:GET /apache_pb.gif HTTP/1.0\tstatus:200\tsize:2326\treferer:http://www.example.com/start.html\tua:Mozilla/4.08 [en] (Win98; I ;Nav)")
for i := 0; i < b.N; i++ {
parser.ParseLine(line, func(label []byte, value []byte) error {
return nil
})
}
}
2 changes: 1 addition & 1 deletion static.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func ParseField(field []byte) (label []byte, value []byte, err error) {

// ParseLine parse one line of LTSV-encoded data and call callback.
// The callback function will be called for each field.
func ParseLine(line []byte, callback func(label []byte, value []byte)) error {
func ParseLine(line []byte, callback func(label []byte, value []byte) error) error {
return DefaultParser.ParseLine(line, callback)
}

Expand Down

0 comments on commit ce19dd1

Please sign in to comment.