feat(pkg/csv2lp): add SkipHeaderLinesReader
parent
bfba3480fb
commit
8dcbd15df2
|
@ -0,0 +1,54 @@
|
|||
package csv2lp
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// skipFirstLines is an io.Reader that skips first lines
|
||||
type skipFirstLines struct {
|
||||
reader io.Reader
|
||||
skipLines int
|
||||
// line is a mutable variable that increases until skipLines is reached
|
||||
line int
|
||||
}
|
||||
|
||||
// Read implements io.Reader
|
||||
func (state *skipFirstLines) Read(p []byte) (n int, err error) {
|
||||
skipHeaderLines:
|
||||
for state.line < state.skipLines {
|
||||
n, err := state.reader.Read(p)
|
||||
if n == 0 {
|
||||
return n, err
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
if p[i] == '\n' {
|
||||
state.line++
|
||||
if state.line == state.skipLines {
|
||||
// modify the buffer and return
|
||||
if i == n-1 {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// continue with the next chunk
|
||||
break skipHeaderLines
|
||||
} else {
|
||||
// copy all bytes after the newline
|
||||
for j := i + 1; j < n; j++ {
|
||||
p[j-i-1] = p[j]
|
||||
}
|
||||
return n - i - 1, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return state.reader.Read(p)
|
||||
}
|
||||
|
||||
// SkipHeaderLinesReader wraps a reader to skip the first skipLines lines
|
||||
func SkipHeaderLinesReader(skipLines int, reader io.Reader) io.Reader {
|
||||
return &skipFirstLines{
|
||||
skipLines: skipLines,
|
||||
reader: reader,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package csv2lp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// simulates the reader that returns all data together with EOF
|
||||
type readOnceWithEOF struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func (r *readOnceWithEOF) Read(p []byte) (n int, err error) {
|
||||
n, _ = r.reader.Read(p)
|
||||
return n, io.EOF
|
||||
}
|
||||
|
||||
// Test_SkipHeaderLines checks that first lines are skipped
|
||||
func Test_SkipHeaderLines(t *testing.T) {
|
||||
input := "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n"
|
||||
|
||||
var tests = []struct {
|
||||
skipCount int
|
||||
result string
|
||||
}{
|
||||
{
|
||||
10,
|
||||
"",
|
||||
},
|
||||
{
|
||||
0,
|
||||
"1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n",
|
||||
},
|
||||
{
|
||||
1,
|
||||
"2\n3\n4\n5\n6\n7\n8\n9\n0\n",
|
||||
},
|
||||
{
|
||||
5,
|
||||
"6\n7\n8\n9\n0\n",
|
||||
},
|
||||
{
|
||||
20,
|
||||
"",
|
||||
},
|
||||
}
|
||||
bufferSizes := []int{1, 2, 7, 0, len(input), len(input) + 1}
|
||||
|
||||
for i, test := range tests {
|
||||
for _, bufferSize := range bufferSizes {
|
||||
t.Run(strconv.Itoa(i)+"_"+strconv.Itoa(bufferSize), func(t *testing.T) {
|
||||
var reader io.Reader
|
||||
if bufferSize == 0 {
|
||||
// emulate a reader that returns EOF together with data
|
||||
bufferSize = len(input)
|
||||
reader = SkipHeaderLinesReader(test.skipCount, &readOnceWithEOF{strings.NewReader(input)})
|
||||
} else {
|
||||
reader = SkipHeaderLinesReader(test.skipCount, strings.NewReader(input))
|
||||
}
|
||||
buffer := make([]byte, bufferSize)
|
||||
result := make([]byte, 0, 100)
|
||||
for {
|
||||
n, err := reader.Read(buffer)
|
||||
if n > 0 {
|
||||
result = append(result, buffer[:n]...)
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
require.Nil(t, err.Error())
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
require.Equal(t, test.result, string(result))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue