go.ndb/write.go
2025-05-08 22:01:09 +03:00

137 lines
2.7 KiB
Go

package ndb
import (
"bytes"
"fmt"
"reflect"
"unicode"
"unicode/utf8"
)
func (e *Encoder) encodeSlice(val reflect.Value) error {
for i := 0; i < val.Len(); i++ {
e.Encode(val.Index(i).Interface())
}
return nil
}
func (e *Encoder) encodeStruct(val reflect.Value) error {
typ := val.Type()
for i := 0; i < typ.NumField(); i++ {
ft := typ.Field(i)
attr := ft.Name
if tag := ft.Tag.Get("ndb"); tag != "" {
attr = tag
}
err := e.writeTuple(attr, val.Field(i))
if err != nil {
return err
}
}
return nil
}
func (e *Encoder) encodeMap(val reflect.Value) error {
for _, k := range val.MapKeys() {
v := val.MapIndex(k)
println(v.Elem().Kind().String())
if v.Elem().Kind() == reflect.Map {
return e.encodeMap(v)
}
if err := e.writeTuple(k.Interface(), v); err != nil {
return err
}
}
return nil
}
func (e *Encoder) writeTuple(k interface{}, v reflect.Value) error {
var values reflect.Value
var attrBuf, valBuf bytes.Buffer
fmt.Fprint(&attrBuf, k)
attr := attrBuf.Bytes()
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
sliceType := reflect.SliceOf(v.Type())
pv := reflect.New(sliceType)
pv.Elem().Set(reflect.MakeSlice(sliceType, 0, 1))
pv.Elem().Set(reflect.Append(pv.Elem(), v))
values = pv.Elem()
} else {
values = v
}
for i := 0; i < values.Len(); i++ {
fmt.Fprint(&valBuf, values.Index(i).Interface())
val := valBuf.Bytes()
if e.start {
if _, err := e.out.Write([]byte{' '}); err != nil {
return err
}
} else {
e.start = true
}
if !validAttr(attr) {
return &SyntaxError{nil, 0, fmt.Sprintf("Invalid attribute %s", attr)}
}
if !validVal(val) {
return &SyntaxError{nil, 0, fmt.Sprintf("Invalid value %s", val)}
}
if bytes.IndexByte(val, '\'') != -1 {
val = bytes.Replace(val, []byte{'\''}, []byte{'\'', '\''}, -1)
}
if _, err := e.out.Write(attr); err != nil {
return err
}
if _, err := e.out.Write([]byte{'='}); err != nil {
return err
}
x := bytes.IndexFunc(val, func(r rune) bool {
return unicode.IsSpace(r)
})
if x != -1 {
if _, err := e.out.Write([]byte{'\''}); err != nil {
return err
}
}
if _, err := e.out.Write(val); err != nil {
return err
}
if x != -1 {
if _, err := e.out.Write([]byte{'\''}); err != nil {
return err
}
}
valBuf.Reset()
}
return nil
}
func validAttr(attr []byte) bool {
if !utf8.Valid(attr) {
return false
}
x := bytes.IndexFunc(attr, func(r rune) bool {
switch {
case r == '\'':
return true
case unicode.IsSpace(r):
return true
}
return !unicode.IsLetter(r) &&
!unicode.IsNumber(r) &&
r != '-'
})
return x == -1
}
func validVal(val []byte) bool {
if !utf8.Valid(val) {
return false
}
return bytes.IndexByte(val, '\n') == -1
}