1
0
Fork 0
mirror of https://github.com/imjasonh/csvstruct synced 2026-07-08 02:55:12 +00:00
csvstruct/encode.go

104 lines
2.1 KiB
Go

package csvstruct
import (
"encoding/csv"
"fmt"
"io"
"reflect"
)
// Encoder encodes and writes CSV rows to an output stream.
type Encoder interface {
EncodeNext(v interface{}) error
}
type encoder struct {
w csv.Writer
hm map[string]int
}
// NewEncoder returns an encoder that writes to w.
func NewEncoder(w io.Writer) Encoder {
return &encoder{w: *csv.NewWriter(w)}
}
// EncodeNext writes the CSV encoding of v to the stream.
func (e *encoder) EncodeNext(v interface{}) error {
if v == nil {
return nil
}
t := reflect.ValueOf(v).Type()
if e.hm == nil {
e.hm = make(map[string]int)
headers := []string{}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.PkgPath != "" { // Filter unexported fields
continue
}
n := f.Name
if f.Tag.Get("csv") != "" {
n = f.Tag.Get("csv")
if n == "-" {
continue
}
}
headers = append(headers, n)
e.hm[n] = i
}
if err := e.w.Write(headers); err != nil {
return err
}
}
rv := reflect.ValueOf(v)
row := []string{}
add := false // Whether there has been a row to write in this call.
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.PkgPath != "" { // Filter unexported fields
continue
}
n := f.Name
if f.Tag.Get("csv") != "" {
n = f.Tag.Get("csv")
}
fi, ok := e.hm[n]
if !ok {
// Unmapped header value
continue
}
// Increase the row size to fit the new row.
for fi >= len(row) {
row = append(row, "")
}
add = true
vf := rv.Field(i)
switch vf.Kind() {
case reflect.String:
row[fi] = vf.String()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
row[fi] = fmt.Sprintf("%d", vf.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
row[fi] = fmt.Sprintf("%d", vf.Uint())
case reflect.Float64:
row[fi] = fmt.Sprintf("%f", vf.Float())
case reflect.Bool:
row[fi] = fmt.Sprintf("%t", vf.Bool())
default:
return fmt.Errorf("can't decode type %v", f.Type)
}
}
if !add {
return nil
}
if err := e.w.Write(row); err != nil {
return err
}
e.w.Flush()
return e.w.Error()
}