Files
kubesphere/vendor/github.com/jszwec/csvutil/encode.go
2021-03-16 10:27:20 +08:00

246 lines
6.0 KiB
Go

package csvutil
import (
"encoding"
"encoding/base64"
"reflect"
"strconv"
)
var (
textMarshaler = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
csvMarshaler = reflect.TypeOf((*Marshaler)(nil)).Elem()
)
var (
encodeFloat32 = encodeFloatN(32)
encodeFloat64 = encodeFloatN(64)
)
type encodeFunc func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error)
func encodeFuncValue(fn reflect.Value) encodeFunc {
return func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
out := fn.Call([]reflect.Value{v})
err, _ := out[1].Interface().(error)
if err != nil {
return nil, err
}
return append(buf, out[0].Bytes()...), nil
}
}
func encodeFuncValuePtr(fn reflect.Value) encodeFunc {
return func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
if !v.CanAddr() {
fallback, err := encodeFn(v.Type(), false, nil, nil)
if err != nil {
return nil, err
}
return fallback(buf, v, omitempty)
}
out := fn.Call([]reflect.Value{v.Addr()})
err, _ := out[1].Interface().(error)
if err != nil {
return nil, err
}
return append(buf, out[0].Bytes()...), nil
}
}
func encodeString(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
return append(buf, v.String()...), nil
}
func encodeInt(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
n := v.Int()
if n == 0 && omitempty {
return buf, nil
}
return strconv.AppendInt(buf, n, 10), nil
}
func encodeUint(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
n := v.Uint()
if n == 0 && omitempty {
return buf, nil
}
return strconv.AppendUint(buf, n, 10), nil
}
func encodeFloatN(bits int) encodeFunc {
return func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
f := v.Float()
if f == 0 && omitempty {
return buf, nil
}
return strconv.AppendFloat(buf, f, 'G', -1, bits), nil
}
}
func encodeBool(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
t := v.Bool()
if !t && omitempty {
return buf, nil
}
return strconv.AppendBool(buf, t), nil
}
func encodeInterface(funcMap map[reflect.Type]reflect.Value, funcs []reflect.Value) encodeFunc {
return func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
if !v.IsValid() || v.IsNil() || !v.Elem().IsValid() {
return buf, nil
}
v = v.Elem()
canAddr := v.Kind() == reflect.Ptr
switch v.Kind() {
case reflect.Ptr, reflect.Interface:
if v.IsNil() {
return buf, nil
}
default:
}
enc, err := encodeFn(v.Type(), canAddr, funcMap, funcs)
if err != nil {
return nil, err
}
return enc(buf, v, omitempty)
}
}
func encodePtrMarshaler(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
if v.CanAddr() {
return encodeMarshaler(buf, v.Addr(), omitempty)
}
fallback, err := encodeFn(v.Type(), false, nil, nil)
if err != nil {
return nil, err
}
return fallback(buf, v, omitempty)
}
func encodeTextMarshaler(buf []byte, v reflect.Value, _ bool) ([]byte, error) {
if v.Kind() == reflect.Ptr && v.IsNil() {
return buf, nil
}
b, err := v.Interface().(encoding.TextMarshaler).MarshalText()
if err != nil {
return nil, &MarshalerError{Type: v.Type(), MarshalerType: "MarshalText", Err: err}
}
return append(buf, b...), nil
}
func encodePtrTextMarshaler(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
if v.CanAddr() {
return encodeTextMarshaler(buf, v.Addr(), omitempty)
}
fallback, err := encodeFn(v.Type(), false, nil, nil)
if err != nil {
return nil, err
}
return fallback(buf, v, omitempty)
}
func encodeMarshaler(buf []byte, v reflect.Value, _ bool) ([]byte, error) {
if v.Kind() == reflect.Ptr && v.IsNil() {
return buf, nil
}
b, err := v.Interface().(Marshaler).MarshalCSV()
if err != nil {
return nil, &MarshalerError{Type: v.Type(), MarshalerType: "MarshalCSV", Err: err}
}
return append(buf, b...), nil
}
func encodePtr(typ reflect.Type, canAddr bool, funcMap map[reflect.Type]reflect.Value, funcs []reflect.Value) (encodeFunc, error) {
next, err := encodeFn(typ.Elem(), canAddr, funcMap, funcs)
if err != nil {
return nil, err
}
return func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
if v.IsNil() {
return buf, nil
}
return next(buf, v.Elem(), omitempty)
}, nil
}
func encodeBytes(buf []byte, v reflect.Value, _ bool) ([]byte, error) {
data := v.Bytes()
l := len(buf)
buf = append(buf, make([]byte, base64.StdEncoding.EncodedLen(len(data)))...)
base64.StdEncoding.Encode(buf[l:], data)
return buf, nil
}
func encodeFn(typ reflect.Type, canAddr bool, funcMap map[reflect.Type]reflect.Value, funcs []reflect.Value) (encodeFunc, error) {
if v, ok := funcMap[typ]; ok {
return encodeFuncValue(v), nil
}
if v, ok := funcMap[reflect.PtrTo(typ)]; ok && canAddr {
return encodeFuncValuePtr(v), nil
}
for _, v := range funcs {
argType := v.Type().In(0)
if typ.AssignableTo(argType) {
return encodeFuncValue(v), nil
}
if canAddr && reflect.PtrTo(typ).AssignableTo(argType) {
return encodeFuncValuePtr(v), nil
}
}
if typ.Implements(csvMarshaler) {
return encodeMarshaler, nil
}
if canAddr && reflect.PtrTo(typ).Implements(csvMarshaler) {
return encodePtrMarshaler, nil
}
if typ.Implements(textMarshaler) {
return encodeTextMarshaler, nil
}
if canAddr && reflect.PtrTo(typ).Implements(textMarshaler) {
return encodePtrTextMarshaler, nil
}
switch typ.Kind() {
case reflect.String:
return encodeString, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return encodeInt, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return encodeUint, nil
case reflect.Float32:
return encodeFloat32, nil
case reflect.Float64:
return encodeFloat64, nil
case reflect.Bool:
return encodeBool, nil
case reflect.Interface:
return encodeInterface(funcMap, funcs), nil
case reflect.Ptr:
return encodePtr(typ, canAddr, funcMap, funcs)
case reflect.Slice:
if typ.Elem().Kind() == reflect.Uint8 {
return encodeBytes, nil
}
}
return nil, &UnsupportedTypeError{Type: typ}
}