Skip to content

Commit

Permalink
🌱 cron: make CSV header optional (3/n) (#2261)
Browse files Browse the repository at this point in the history
* Make CSV header optional.

Signed-off-by: Spencer Schrock <[email protected]>

* Appease linter.

Signed-off-by: Spencer Schrock <[email protected]>

* Address PR feedback.

Signed-off-by: Spencer Schrock <[email protected]>

Signed-off-by: Spencer Schrock <[email protected]>
  • Loading branch information
spencerschrock authored Sep 14, 2022
1 parent bde0ae1 commit 2231d1f
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 4 deletions.
33 changes: 29 additions & 4 deletions cron/internal/data/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"io"
"reflect"

"github.com/jszwec/csvutil"

Expand All @@ -36,21 +37,45 @@ type Iterator interface {
func MakeIteratorFrom(reader io.Reader) (Iterator, error) {
csvReader := csv.NewReader(reader)
csvReader.Comment = '#'
dec, err := csvutil.NewDecoder(csvReader)
header, err := csvutil.Header(RepoFormat{}, "csv")
if err != nil {
return nil, fmt.Errorf("error in csvutil.Header: %w", err)
}
dec, err := csvutil.NewDecoder(csvReader, header...)
if err != nil {
return nil, fmt.Errorf("error in csvutil.NewDecoder: %w", err)
}
return &csvIterator{decoder: dec}, nil
}

type csvIterator struct {
decoder *csvutil.Decoder
err error
next RepoFormat
decoder *csvutil.Decoder
err error
next RepoFormat
afterHeader bool
}

// returns true on the first call if the most recently decoded record is a header.
// always returns false on subsequent calls, as this is only intended to evaluate the first line.
func (reader *csvIterator) isHeader() bool {
if reader.afterHeader {
return false
}
header, err := csvutil.Header(RepoFormat{}, "csv")
if err != nil {
reader.err = err
return false
}
lastRead := reader.decoder.Record()
reader.afterHeader = true
return reflect.DeepEqual(header, lastRead)
}

func (reader *csvIterator) HasNext() bool {
reader.err = reader.decoder.Decode(&reader.next)
if reader.isHeader() {
reader.err = reader.decoder.Decode(&reader.next)
}
return !errors.Is(reader.err, io.EOF)
}

Expand Down
36 changes: 36 additions & 0 deletions cron/internal/data/iterator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,42 @@ func TestCsvIterator(t *testing.T) {
},
},
},
{
name: "Ignore First Header Row",
filename: "testdata/ignore_header.csv",
outcomes: []outcome{
{
hasError: false,
repo: RepoFormat{
Repo: "github.com/owner1/repo1",
},
},
{
// will error due to GitHub URL sanity check
hasError: true,
repo: RepoFormat{
Repo: "repo",
},
},
},
},
{
name: "No Header Row",
filename: "testdata/no_header.csv",
outcomes: []outcome{
{
hasError: false,
repo: RepoFormat{
Repo: "github.com/owner1/repo1",
},
},
},
},
{
name: "Only Header Row",
filename: "testdata/only_header.csv",
outcomes: []outcome{},
},
}

for _, testcase := range testcases {
Expand Down
3 changes: 3 additions & 0 deletions cron/internal/data/testdata/ignore_header.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
repo,metadata
github.com/owner1/repo1,
repo,metadata
1 change: 1 addition & 0 deletions cron/internal/data/testdata/no_header.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github.com/owner1/repo1,
1 change: 1 addition & 0 deletions cron/internal/data/testdata/only_header.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
repo,metadata

0 comments on commit 2231d1f

Please sign in to comment.