wip: unmarshal basic functionality works in debug

This commit is contained in:
Suyono 2023-06-25 22:14:31 +10:00
parent 5b9b896f34
commit 8a84f5164d
4 changed files with 260 additions and 22 deletions

View File

@ -1,8 +1,10 @@
package gocsvparser
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
)
@ -26,7 +28,12 @@ func newDefaultHandler() *defaultRecordHandler {
func (d *defaultRecordHandler) HandleRecord(v any, record []string) error {
var (
err error
err error
strField string
index int
ok bool
binding columnFieldBinding
val reflect.Value
)
if d.outType == nil {
@ -34,20 +41,116 @@ func (d *defaultRecordHandler) HandleRecord(v any, record []string) error {
if err != nil {
return fmt.Errorf("error HandleRecord: detType: %+v", err)
}
err = d.mapStructTag()
if err != nil {
return fmt.Errorf("error HandleRecord: mapStructTag: %+v", err)
}
}
if !d.columnNameMapped {
for index, strField = range record {
if binding, ok = d.fieldByTag[strField]; ok {
binding.index = index
d.fieldByTag[strField] = binding
}
}
d.columnNameMapped = true
return HeaderRead
}
val = reflect.ValueOf(v)
if val.Kind() != reflect.Pointer {
return errors.New("error HandleRecord: v Kind() is not Pointer")
}
val = val.Elem()
switch val.Kind() {
case reflect.Struct:
for _, binding = range d.fieldByTag {
if binding.index < len(record) {
val, err = d.setValue(val, record[binding.index], binding.field)
if err != nil {
return fmt.Errorf("error HandleRecord: StructField SetValue: %+v", err)
}
}
}
case reflect.Map:
return errors.New("error HandleRecord: Map is not supported yet") //TODO: fix me
case reflect.Slice:
return errors.New("error HandleRecord: Slice is not supported yet") //TODO: fix me
}
return nil
}
func (d *defaultRecordHandler) setValue(val reflect.Value, strValue string, structFiled reflect.StructField) (reflect.Value, error) {
var (
fieldVal reflect.Value
f64 float64
i64 int64
i int
err error
b bool
)
fieldVal = val.FieldByIndex(structFiled.Index)
switch fieldVal.Type().Kind() {
case reflect.String:
fieldVal.SetString(strValue)
case reflect.Bool:
b, err = strconv.ParseBool(strValue)
if err != nil {
return reflect.Value{}, fmt.Errorf("ParseBool: %+v", err)
}
fieldVal.SetBool(b)
case reflect.Int64:
i64, err = strconv.ParseInt(strValue, 0, 64)
if err != nil {
return reflect.Value{}, fmt.Errorf("ParseInt 64: %+v", err)
}
fieldVal.SetInt(i64)
case reflect.Int32:
i64, err = strconv.ParseInt(strValue, 0, 32)
if err != nil {
return reflect.Value{}, fmt.Errorf("ParseInt 32: %+v", err)
}
fieldVal.SetInt(i64)
case reflect.Int16:
i64, err = strconv.ParseInt(strValue, 0, 16)
if err != nil {
return reflect.Value{}, fmt.Errorf("ParseInt 16: %+v", err)
}
fieldVal.SetInt(i64)
case reflect.Int8:
i64, err = strconv.ParseInt(strValue, 0, 8)
if err != nil {
return reflect.Value{}, fmt.Errorf("ParseInt 8: %+v", err)
}
fieldVal.SetInt(i64)
case reflect.Int:
i, err = strconv.Atoi(strValue)
if err != nil {
return reflect.Value{}, fmt.Errorf("strconv.Atoi: %+v", err)
}
fieldVal.SetInt(int64(i))
case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint:
return reflect.Value{}, errors.New("unimplemented") //TODO: fix me
case reflect.Float32:
f64, err = strconv.ParseFloat(strValue, 32)
if err != nil {
return reflect.Value{}, fmt.Errorf("ParseFloat 32: %+v", err)
}
fieldVal.SetFloat(f64)
case reflect.Float64:
f64, err = strconv.ParseFloat(strValue, 64)
if err != nil {
return reflect.Value{}, fmt.Errorf("ParseFloat 64: %+v", err)
}
fieldVal.SetFloat(f64)
default:
return reflect.Value{}, errors.New("missing implementation") //TODO: fix me
}
return val, nil
}
func (d *defaultRecordHandler) SetFieldConfigs(configs []FieldsConfig) {
}

View File

@ -1 +1,38 @@
package example
import (
"testing"
"github.com/budiuno/gocsvparser"
)
type mtcarsFlat struct {
Model string `csv:"model"`
MilesPerGalon float64 `csv:"mpg"`
Cylinder int `csv:"cyl"`
Displacement float64 `csv:"disp"`
Horsepower int `csv:"hp"`
DriveShaftRatio float64 `csv:"drat"`
Weight float64 `csv:"wt"`
QuerterMileTime float64 `csv:"qsec"`
VEngine bool `csv:"vs"`
AutoTransmission bool `csv:"am"`
Gear int `csv:"gear"`
Carburetors int `csv:"carb"`
}
func TestMtcars(t *testing.T) {
var (
cars []mtcarsFlat
err error
)
err = gocsvparser.Unmarshal(MtcarsCsv, &cars)
if err != nil {
t.Fatalf("unexpected error %+v", err)
}
for _, c := range cars {
t.Logf("%+v", c)
}
}

View File

@ -1 +1,37 @@
package example
var (
MtcarsCsv = []byte(`model,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb
Mazda RX4,21,6,160,110,3.9,2.62,16.46,0,1,4,4
Mazda RX4 Wag,21,6,160,110,3.9,2.875,17.02,0,1,4,4
Datsun 710,22.8,4,108,93,3.85,2.32,18.61,1,1,4,1
Hornet 4 Drive,21.4,6,258,110,3.08,3.215,19.44,1,0,3,1
Hornet Sportabout,18.7,8,360,175,3.15,3.44,17.02,0,0,3,2
Valiant,18.1,6,225,105,2.76,3.46,20.22,1,0,3,1
Duster 360,14.3,8,360,245,3.21,3.57,15.84,0,0,3,4
Merc 240D,24.4,4,146.7,62,3.69,3.19,20,1,0,4,2
Merc 230,22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2
Merc 280,19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4
Merc 280C,17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4
Merc 450SE,16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3
Merc 450SL,17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3
Merc 450SLC,15.2,8,275.8,180,3.07,3.78,18,0,0,3,3
Cadillac Fleetwood,10.4,8,472,205,2.93,5.25,17.98,0,0,3,4
Lincoln Continental,10.4,8,460,215,3,5.424,17.82,0,0,3,4
Chrysler Imperial,14.7,8,440,230,3.23,5.345,17.42,0,0,3,4
Fiat 128,32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1
Honda Civic,30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2
Toyota Corolla,33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1
Toyota Corona,21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1
Dodge Challenger,15.5,8,318,150,2.76,3.52,16.87,0,0,3,2
AMC Javelin,15.2,8,304,150,3.15,3.435,17.3,0,0,3,2
Camaro Z28,13.3,8,350,245,3.73,3.84,15.41,0,0,3,4
Pontiac Firebird,19.2,8,400,175,3.08,3.845,17.05,0,0,3,2
Fiat X1-9,27.3,4,79,66,4.08,1.935,18.9,1,1,4,1
Porsche 914-2,26,4,120.3,91,4.43,2.14,16.7,0,1,5,2
Lotus Europa,30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2
Ford Pantera L,15.8,8,351,264,4.22,3.17,14.5,0,1,5,4
Ferrari Dino,19.7,6,145,175,3.62,2.77,15.5,0,1,5,6
Maserati Bora,15,8,301,335,3.54,3.57,14.6,0,1,5,8
Volvo 142E,21.4,4,121,109,4.11,2.78,18.6,1,1,4,2`)
)

View File

@ -3,6 +3,14 @@ package gocsvparser
import (
"bytes"
"encoding/csv"
"errors"
"fmt"
"io"
"reflect"
)
var (
HeaderRead = errors.New("column headers successfully read")
)
type FieldsConfig struct {
@ -115,6 +123,13 @@ func (u *Unmarshaler) processOptions(options ...CsvOption) {
}
func (u *Unmarshaler) Unmarshal(data []byte, v any, options ...CsvOption) error {
var (
typ reflect.Type
val, slice reflect.Value
record []string
err error
)
if len(options) > 0 {
u.options = append(u.options, options...)
}
@ -124,9 +139,38 @@ func (u *Unmarshaler) Unmarshal(data []byte, v any, options ...CsvOption) error
if u.handlerType == direct {
if u.recordHandler == nil {
//TODO: build default generator
u.recordHandler = newDefaultHandler()
}
slice, typ, err = u.detOutputType(v)
if err != nil {
return fmt.Errorf("error Unmarshal: detOutputType: %+v", err)
}
for {
record, err = u.csvReader.Read()
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("error Unmarshal: csv.Reader.Read: %+v", err)
}
val, err = u.newElem(typ)
if err != nil {
return fmt.Errorf("error Unmarshal: newElem: %+v", err)
}
err = u.recordHandler.HandleRecord(val.Interface(), record)
if err != nil {
if err == HeaderRead {
continue
}
return fmt.Errorf("error Unmarshal: RecordHandler.HandleRecord: %+v", err)
}
slice.Set(reflect.Append(slice, val.Elem()))
}
} else {
}
@ -135,23 +179,41 @@ func (u *Unmarshaler) Unmarshal(data []byte, v any, options ...CsvOption) error
return nil
}
func (u *Unmarshaler) detOutputType(v any) (reflect.Value, reflect.Type, error) {
var typ reflect.Type
if v == nil {
return reflect.Value{}, nil, errors.New("the output parameter is nil")
}
val := reflect.ValueOf(v)
if val.Kind() != reflect.Pointer {
return reflect.Value{}, nil, errors.New("invalid output parameter type")
}
val = val.Elem()
if val.Kind() != reflect.Slice {
return reflect.Value{}, nil, errors.New("invalid output parameter type")
}
typ = val.Type().Elem()
return val, typ, nil
}
func Unmarshal(data []byte, v any, options ...CsvOption) error {
return NewUnmarshaler().Unmarshal(data, v, options...)
}
// func Read(i interface{}) {
// var val reflect.Value
func (u *Unmarshaler) newElem(typ reflect.Type) (reflect.Value, error) {
switch typ.Kind() {
case reflect.Struct:
return reflect.New(typ), nil
case reflect.Map:
//TODO: implementation
case reflect.Slice:
//TODO: implementation
}
// val = reflect.ValueOf(i)
// if val.Kind() == reflect.Pointer {
// val = val.Elem()
// if val.Kind() == reflect.Slice {
// val = val.Elem()
// if val.Kind() == reflect.Struct {
// fields := reflect.VisibleFields(val.Type())
// }
// }
// }
// }
return reflect.Zero(typ), errors.New("invalid impelementation") //TODO: placeholder; update me
}