Skip to content

Commit

Permalink
Merge pull request #8 from sonirico/feat/csv-streaming-response
Browse files Browse the repository at this point in the history
enhacement: csv parser can handle quotes and separators
  • Loading branch information
sonirico authored Oct 6, 2022
2 parents 78889b0 + fdff1b3 commit aa3a580
Show file tree
Hide file tree
Showing 8 changed files with 543 additions and 201 deletions.
76 changes: 76 additions & 0 deletions csvparser/columns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package csvparser

type (
Col[T any] interface {
Parse(data []byte, item *T) (int, error)
//Compile(x T, writer io.Writer) error
}

opts struct {
sep byte
}

ColFactory[T any] func(opts) Col[T]

StringColumn[T any] struct {
inner StringType
setter func(x *T, v string)
getter func(x T) string
}

IntColumn[T any] struct {
inner IntegerType
setter func(x *T, v int)
getter func(x T) int
}

BoolColumn[T any] struct {
inner StringType
setter func(x T, v bool)
getter func(x T) bool
}
)

func (s StringColumn[T]) Parse(data []byte, item *T) (int, error) {
val, n, err := s.inner.Parse(data)
if err != nil {
return n, err
}
s.setter(item, val)
return n, nil
}

func (c IntColumn[T]) Parse(data []byte, item *T) (int, error) {
val, n, err := c.inner.Parse(data)
if err != nil {
return n, err
}
c.setter(item, val)
return n, nil
}

func StringCol[T any](
quote byte,
getter func(T) string,
setter func(*T, string),
) ColFactory[T] {
return func(opts opts) Col[T] {
return StringColumn[T]{
inner: StrType(quote, opts.sep),
getter: getter, setter: setter,
}
}
}

func IntCol[T any](
quote byte,
getter func(T) int,
setter func(*T, int),
) ColFactory[T] {
return func(opts opts) Col[T] {
return IntColumn[T]{
inner: IntType(quote, opts.sep),
getter: getter, setter: setter,
}
}
}
197 changes: 0 additions & 197 deletions csvparser/csvparser.go

This file was deleted.

1 change: 1 addition & 0 deletions csvparser/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import "github.com/pkg/errors"

var (
ErrColumnMismatch = errors.New("column mismatch")
ErrQuoteExpected = errors.New("quote was expected")
)
64 changes: 64 additions & 0 deletions csvparser/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package csvparser

import (
"bytes"
)

var (
quote = []byte{byte('"')}

QuoteDouble byte = '"'
QuoteSimple byte = '\''
QuoteNone byte = 0

SeparatorComma byte = ','
SeparatorSemicolon byte = ';'
SeparatorTab byte = '\t'
)

type (
Parser[T any] struct {
separator byte
columns []Col[T]
}
)

func (p Parser[T]) Parse(data []byte, item *T) (err error) {
data = bytes.TrimSpace(data) // cleanup phase
sepLen := 1 // len(p.separator)

for i, col := range p.columns {
var read int
read, err = col.Parse(data, item)
if err != nil {
return
}

// TODO: handle read =0
_ = i

if read > len(data) {
break
}

// create a cursor to have better readability under the fact the column types will only parse
// its desired data, letting the parser have the liability to advance de cursor.
cursor := read
if read+sepLen <= len(data) {
cursor += sepLen
}

data = data[cursor:]
}
return nil
}

func New[T any](sep byte, cols ...ColFactory[T]) Parser[T] {
columns := make([]Col[T], len(cols))
opt := opts{sep: sep}

for i, c := range cols {
columns[i] = c(opt)
}
return Parser[T]{separator: sep, columns: columns}
}
Loading

0 comments on commit aa3a580

Please sign in to comment.