All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
103 lines
1.7 KiB
Go
103 lines
1.7 KiB
Go
package csv
|
|
|
|
import (
|
|
"context"
|
|
"encoding/csv"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
type RowHandler func(row []string, rowNumber int) error
|
|
|
|
func ReadCSV(file io.Reader, handler RowHandler) (int, []string, error) {
|
|
return ReadCSVWithContext(context.Background(), file, handler)
|
|
}
|
|
|
|
func ReadCSVWithContext(ctx context.Context, file io.Reader, handler RowHandler) (int, []string, error) {
|
|
reader := csv.NewReader(file)
|
|
reader.LazyQuotes = true
|
|
reader.TrimLeadingSpace = true
|
|
|
|
var errors []string
|
|
rowNumber := 0
|
|
totalRows := 0
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return totalRows, errors, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
row, err := reader.Read()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
rowNumber++
|
|
|
|
if err != nil {
|
|
errors = append(errors, "Row "+itoa(rowNumber)+": "+err.Error())
|
|
continue
|
|
}
|
|
|
|
if len(row) == 0 || (len(row) == 1 && strings.TrimSpace(row[0]) == "") {
|
|
continue
|
|
}
|
|
|
|
if rowNumber == 1 {
|
|
continue
|
|
}
|
|
|
|
if err := handler(row, rowNumber); err != nil {
|
|
if ctx.Err() != nil {
|
|
return totalRows, errors, ctx.Err()
|
|
}
|
|
errors = append(errors, "Row "+itoa(rowNumber)+": "+err.Error())
|
|
}
|
|
totalRows++
|
|
}
|
|
|
|
return totalRows, errors, nil
|
|
}
|
|
|
|
func CountRows(file io.Reader) (int, error) {
|
|
reader := csv.NewReader(file)
|
|
count := 0
|
|
for {
|
|
_, err := reader.Read()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
continue
|
|
}
|
|
count++
|
|
}
|
|
return count, nil
|
|
}
|
|
|
|
func WriteCSV(headers []string, rows [][]string) string {
|
|
var sb strings.Builder
|
|
writer := csv.NewWriter(&sb)
|
|
|
|
writer.Write(headers)
|
|
for _, row := range rows {
|
|
writer.Write(row)
|
|
}
|
|
writer.Flush()
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
func itoa(i int) string {
|
|
if i == 0 {
|
|
return "0"
|
|
}
|
|
result := ""
|
|
for i > 0 {
|
|
result = string(rune('0'+i%10)) + result
|
|
i /= 10
|
|
}
|
|
return result
|
|
}
|