wip: unmarshal basic functionality works in debug
This commit is contained in:
parent
5b9b896f34
commit
8a84f5164d
@ -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) {
|
||||
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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`)
|
||||
)
|
||||
|
||||
94
unmarshal.go
94
unmarshal.go
@ -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
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user