github.com/RevenueMonster/sqlike@v1.0.6/sql/codec/encoder.go (about) 1 package codec 2 3 import ( 4 "bytes" 5 "database/sql" 6 "encoding/base64" 7 "encoding/json" 8 "fmt" 9 "net/url" 10 "reflect" 11 "strconv" 12 "strings" 13 "time" 14 15 "cloud.google.com/go/datastore" 16 "github.com/RevenueMonster/sqlike/reflext" 17 "github.com/RevenueMonster/sqlike/spatial" 18 "github.com/paulmach/orb" 19 "github.com/paulmach/orb/encoding/wkt" 20 21 "github.com/RevenueMonster/sqlike/jsonb" 22 ) 23 24 // DefaultEncoders : 25 type DefaultEncoders struct { 26 codec *Registry 27 } 28 29 // EncodeByte : 30 func (enc DefaultEncoders) EncodeByte(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 31 b := v.Bytes() 32 if b == nil { 33 return make([]byte, 0), nil 34 } 35 x := base64.StdEncoding.EncodeToString(b) 36 return []byte(x), nil 37 } 38 39 // EncodeRawBytes : 40 func (enc DefaultEncoders) EncodeRawBytes(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 41 return sql.RawBytes(v.Bytes()), nil 42 } 43 44 // EncodeJSONRaw : 45 func (enc DefaultEncoders) EncodeJSONRaw(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 46 if v.IsNil() { 47 return []byte("null"), nil 48 } 49 buf := new(bytes.Buffer) 50 if err := json.Compact(buf, v.Bytes()); err != nil { 51 return nil, err 52 } 53 if buf.Len() == 0 { 54 return []byte(`{}`), nil 55 } 56 return json.RawMessage(buf.Bytes()), nil 57 } 58 59 // EncodeStringer : 60 func (enc DefaultEncoders) EncodeStringer(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 61 x := v.Interface().(fmt.Stringer) 62 return x.String(), nil 63 } 64 65 // EncodePointerStringer : 66 func (enc DefaultEncoders) EncodePointerStringer(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 67 x := v.Addr().Interface().(fmt.Stringer) 68 return x.String(), nil 69 } 70 71 // EncodeDateTime : 72 func (enc DefaultEncoders) EncodeDateTime(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 73 x := v.Interface().(time.Time) 74 // if x.IsZero() { 75 // x, _ = time.Parse(time.RFC3339, "1970-01-01T08:00:00Z") 76 // return x, nil 77 // } 78 // convert to UTC before storing into DB 79 return x.UTC(), nil 80 } 81 82 // EncodeSpatial : 83 func (enc DefaultEncoders) EncodeSpatial(st spatial.Type) ValueEncoder { 84 return func(sf reflext.StructFielder, v reflect.Value) (interface{}, error) { 85 if reflext.IsZero(v) { 86 return nil, nil 87 } 88 x := v.Interface().(orb.Geometry) 89 var srid uint 90 if sf != nil { 91 tag, ok := sf.Tag().LookUp("srid") 92 if ok { 93 integer, _ := strconv.Atoi(tag) 94 if integer > 0 { 95 srid = uint(integer) 96 } 97 } 98 } 99 return spatial.Geometry{ 100 Type: st, 101 SRID: srid, 102 WKT: wkt.MarshalString(x), 103 }, nil 104 } 105 } 106 107 // EncodeString : 108 func (enc DefaultEncoders) EncodeString(sf reflext.StructFielder, v reflect.Value) (interface{}, error) { 109 str := v.String() 110 if str == "" && sf != nil { 111 tag := sf.Tag() 112 if val, ok := tag.LookUp("enum"); ok { 113 enums := strings.Split(val, "|") 114 if len(enums) > 0 { 115 return enums[0], nil 116 } 117 } 118 } 119 return str, nil 120 } 121 122 // EncodeBool : 123 func (enc DefaultEncoders) EncodeBool(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 124 return v.Bool(), nil 125 } 126 127 // EncodeInt : 128 func (enc DefaultEncoders) EncodeInt(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 129 return v.Int(), nil 130 } 131 132 // EncodeUint : 133 func (enc DefaultEncoders) EncodeUint(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 134 return v.Uint(), nil 135 } 136 137 // EncodeFloat : 138 func (enc DefaultEncoders) EncodeFloat(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 139 return v.Float(), nil 140 } 141 142 // EncodePtr : 143 func (enc *DefaultEncoders) EncodePtr(sf reflext.StructFielder, v reflect.Value) (interface{}, error) { 144 if !v.IsValid() || v.IsNil() { 145 return nil, nil 146 } 147 v = v.Elem() 148 encoder, err := enc.codec.LookupEncoder(v) 149 if err != nil { 150 return nil, err 151 } 152 return encoder(sf, v) 153 } 154 155 // EncodeStruct : 156 func (enc DefaultEncoders) EncodeStruct(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 157 return jsonb.Marshal(v) 158 } 159 160 // EncodeArray : 161 func (enc DefaultEncoders) EncodeArray(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 162 return jsonb.Marshal(v) 163 } 164 165 // EncodeMap : 166 func (enc DefaultEncoders) EncodeMap(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 167 if v.IsNil() { 168 return string("null"), nil 169 } 170 171 // t := v.Type() 172 // k := t.Key() 173 // if k.Kind() != reflect.String { 174 // return nil, fmt.Errorf("codec: unsupported data type %q for map key, it must be string", k.Kind()) 175 // } 176 // k = t.Elem() 177 // if !isBaseType(k) { 178 // return nil, fmt.Errorf("codec: unsupported data type %q for map value", k.Kind()) 179 // } 180 return jsonb.Marshal(v) 181 } 182 183 // func isBaseType(t reflect.Type) bool { 184 // for { 185 // k := t.Kind() 186 // switch k { 187 // case reflect.String: 188 // return true 189 // case reflect.Bool: 190 // return true 191 // case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 192 // return true 193 // case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 194 // return true 195 // case reflect.Float32, reflect.Float64: 196 // return true 197 // case reflect.Ptr: 198 // t = t.Elem() 199 // default: 200 // return false 201 // } 202 // } 203 // } 204 205 func (enc DefaultEncoders) EncodeDatastoreKey(_ reflext.StructFielder, v reflect.Value) (interface{}, error) { 206 key := v.Interface().(datastore.Key) 207 kk, pp := splitKey(&key) 208 return strings.Trim(pp+"/"+kk, "/"), nil 209 } 210 211 func splitKey(k *datastore.Key) (key string, parent string) { 212 if k == nil { 213 return "", "" 214 } 215 if k.ID > 0 { 216 return strconv.FormatInt(k.ID, 10), stringifyKey(k.Parent) 217 } 218 name := url.PathEscape(k.Name) 219 return "'" + name + "'", stringifyKey(k.Parent) 220 } 221 222 func stringifyKey(key *datastore.Key) string { 223 paths := make([]string, 0) 224 parentKey := key 225 226 for parentKey != nil { 227 var k string 228 if parentKey.Name != "" { 229 name := url.PathEscape(parentKey.Name) 230 k = parentKey.Kind + ",'" + name + "'" 231 } else { 232 k = parentKey.Kind + "," + strconv.FormatInt(parentKey.ID, 10) 233 } 234 paths = append([]string{k}, paths...) 235 parentKey = parentKey.Parent 236 } 237 238 if len(paths) > 0 { 239 return strings.Join(paths, "/") 240 } 241 242 return "" 243 }