gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/pseudotypes.go (about) 1 package rethinkdb 2 3 import ( 4 "encoding/base64" 5 "math" 6 "strconv" 7 "time" 8 9 "gopkg.in/rethinkdb/rethinkdb-go.v6/types" 10 11 "fmt" 12 ) 13 14 func convertPseudotype(obj map[string]interface{}, opts map[string]interface{}) (interface{}, error) { 15 if reqlType, ok := obj["$reql_type$"]; ok { 16 if reqlType == "TIME" { 17 // load timeFormat, set to native if the option was not set 18 timeFormat := "native" 19 if opt, ok := opts["time_format"]; ok { 20 if sopt, ok := opt.(string); ok { 21 timeFormat = sopt 22 } else { 23 return nil, fmt.Errorf("Invalid time_format run option \"%s\".", opt) 24 } 25 } 26 27 if timeFormat == "native" { 28 return reqlTimeToNativeTime(obj["epoch_time"].(float64), obj["timezone"].(string)) 29 } else if timeFormat == "raw" { 30 return obj, nil 31 } else { 32 return nil, fmt.Errorf("Unknown time_format run option \"%s\".", reqlType) 33 } 34 } else if reqlType == "GROUPED_DATA" { 35 // load groupFormat, set to native if the option was not set 36 groupFormat := "native" 37 if opt, ok := opts["group_format"]; ok { 38 if sopt, ok := opt.(string); ok { 39 groupFormat = sopt 40 } else { 41 return nil, fmt.Errorf("Invalid group_format run option \"%s\".", opt) 42 } 43 } 44 45 if groupFormat == "native" || groupFormat == "slice" { 46 return reqlGroupedDataToSlice(obj) 47 } else if groupFormat == "map" { 48 return reqlGroupedDataToMap(obj) 49 } else if groupFormat == "raw" { 50 return obj, nil 51 } else { 52 return nil, fmt.Errorf("Unknown group_format run option \"%s\".", reqlType) 53 } 54 } else if reqlType == "BINARY" { 55 binaryFormat := "native" 56 if opt, ok := opts["binary_format"]; ok { 57 if sopt, ok := opt.(string); ok { 58 binaryFormat = sopt 59 } else { 60 return nil, fmt.Errorf("Invalid binary_format run option \"%s\".", opt) 61 } 62 } 63 64 if binaryFormat == "native" { 65 return reqlBinaryToNativeBytes(obj) 66 } else if binaryFormat == "raw" { 67 return obj, nil 68 } else { 69 return nil, fmt.Errorf("Unknown binary_format run option \"%s\".", reqlType) 70 } 71 } else if reqlType == "GEOMETRY" { 72 geometryFormat := "native" 73 if opt, ok := opts["geometry_format"]; ok { 74 if sopt, ok := opt.(string); ok { 75 geometryFormat = sopt 76 } else { 77 return nil, fmt.Errorf("Invalid geometry_format run option \"%s\".", opt) 78 } 79 } 80 81 if geometryFormat == "native" { 82 return reqlGeometryToNativeGeometry(obj) 83 } else if geometryFormat == "raw" { 84 return obj, nil 85 } else { 86 return nil, fmt.Errorf("Unknown geometry_format run option \"%s\".", reqlType) 87 } 88 } else { 89 return obj, nil 90 } 91 } 92 93 return obj, nil 94 } 95 96 func recursivelyConvertPseudotype(obj interface{}, opts map[string]interface{}) (interface{}, error) { 97 var err error 98 99 switch obj := obj.(type) { 100 case []interface{}: 101 for key, val := range obj { 102 obj[key], err = recursivelyConvertPseudotype(val, opts) 103 if err != nil { 104 return nil, err 105 } 106 } 107 case map[string]interface{}: 108 for key, val := range obj { 109 obj[key], err = recursivelyConvertPseudotype(val, opts) 110 if err != nil { 111 return nil, err 112 } 113 } 114 115 pobj, err := convertPseudotype(obj, opts) 116 if err != nil { 117 return nil, err 118 } 119 120 return pobj, nil 121 } 122 123 return obj, nil 124 } 125 126 // Pseudo-type helper functions 127 128 func reqlTimeToNativeTime(timestamp float64, timezone string) (time.Time, error) { 129 sec, ms := math.Modf(timestamp) 130 131 // Convert to native time rounding to milliseconds 132 t := time.Unix(int64(sec), int64(math.Floor(ms*1000+0.5))*1000*1000) 133 134 // Caclulate the timezone 135 if timezone != "" { 136 hours, err := strconv.Atoi(timezone[1:3]) 137 if err != nil { 138 return time.Time{}, err 139 } 140 minutes, err := strconv.Atoi(timezone[4:6]) 141 if err != nil { 142 return time.Time{}, err 143 } 144 tzOffset := ((hours * 60) + minutes) * 60 145 if timezone[:1] == "-" { 146 tzOffset = 0 - tzOffset 147 } 148 149 t = t.In(time.FixedZone(timezone, tzOffset)) 150 } 151 152 return t, nil 153 } 154 155 func reqlGroupedDataToSlice(obj map[string]interface{}) (interface{}, error) { 156 if data, ok := obj["data"]; ok { 157 ret := []interface{}{} 158 for _, v := range data.([]interface{}) { 159 v := v.([]interface{}) 160 ret = append(ret, map[string]interface{}{ 161 "group": v[0], 162 "reduction": v[1], 163 }) 164 } 165 return ret, nil 166 } 167 return nil, fmt.Errorf("pseudo-type GROUPED_DATA object %v does not have the expected field \"data\"", obj) 168 } 169 170 func reqlGroupedDataToMap(obj map[string]interface{}) (interface{}, error) { 171 if data, ok := obj["data"]; ok { 172 ret := map[interface{}]interface{}{} 173 for _, v := range data.([]interface{}) { 174 v := v.([]interface{}) 175 ret[v[0]] = v[1] 176 } 177 return ret, nil 178 } 179 return nil, fmt.Errorf("pseudo-type GROUPED_DATA object %v does not have the expected field \"data\"", obj) 180 } 181 182 func reqlBinaryToNativeBytes(obj map[string]interface{}) (interface{}, error) { 183 if data, ok := obj["data"]; ok { 184 if data, ok := data.(string); ok { 185 b, err := base64.StdEncoding.DecodeString(data) 186 if err != nil { 187 return nil, fmt.Errorf("error decoding pseudo-type BINARY object %v", obj) 188 } 189 190 return b, nil 191 } 192 return nil, fmt.Errorf("pseudo-type BINARY object %v field \"data\" is not valid", obj) 193 } 194 return nil, fmt.Errorf("pseudo-type BINARY object %v does not have the expected field \"data\"", obj) 195 } 196 197 func reqlGeometryToNativeGeometry(obj map[string]interface{}) (interface{}, error) { 198 if typ, ok := obj["type"]; !ok { 199 return nil, fmt.Errorf("pseudo-type GEOMETRY object %v does not have the expected field \"type\"", obj) 200 } else if typ, ok := typ.(string); !ok { 201 return nil, fmt.Errorf("pseudo-type GEOMETRY object %v field \"type\" is not valid", obj) 202 } else if coords, ok := obj["coordinates"]; !ok { 203 return nil, fmt.Errorf("pseudo-type GEOMETRY object %v does not have the expected field \"coordinates\"", obj) 204 } else if typ == "Point" { 205 point, err := types.UnmarshalPoint(coords) 206 if err != nil { 207 return nil, err 208 } 209 210 return types.Geometry{ 211 Type: "Point", 212 Point: point, 213 }, nil 214 } else if typ == "LineString" { 215 line, err := types.UnmarshalLineString(coords) 216 if err != nil { 217 return nil, err 218 } 219 return types.Geometry{ 220 Type: "LineString", 221 Line: line, 222 }, nil 223 } else if typ == "Polygon" { 224 lines, err := types.UnmarshalPolygon(coords) 225 if err != nil { 226 return nil, err 227 } 228 return types.Geometry{ 229 Type: "Polygon", 230 Lines: lines, 231 }, nil 232 } else { 233 return nil, fmt.Errorf("pseudo-type GEOMETRY object %v field has unknown type %s", obj, typ) 234 } 235 }