github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/datacodec/extractors.go (about) 1 // Copyright 2021 DataStax 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package datacodec 16 17 import ( 18 "errors" 19 "reflect" 20 "strings" 21 ) 22 23 // A utility to extract elements from container types like slices, arrays, structs and maps, using a unified API. 24 type extractor interface { 25 26 // getElem returns the element at the given key. 27 getElem(index int, key interface{}) (interface{}, error) 28 } 29 30 // A utility to extract keys from maps. 31 type keyValueExtractor interface { 32 extractor 33 34 // getKey returns the key at the given index. 35 getKey(index int) interface{} 36 } 37 38 type sliceExtractor struct { 39 source reflect.Value 40 } 41 42 type mapExtractor struct { 43 source reflect.Value 44 keys []reflect.Value // to keep iteration order constant 45 } 46 47 type structExtractor struct { 48 source reflect.Value 49 } 50 51 func newSliceExtractor(source reflect.Value) (extractor, error) { 52 if source.Kind() != reflect.Slice && source.Kind() != reflect.Array { 53 return nil, errors.New("expected slice or array, got: " + source.Type().String()) 54 } else if source.Kind() == reflect.Slice && source.IsNil() { 55 return nil, errors.New("slice is nil") 56 } 57 return &sliceExtractor{source}, nil 58 } 59 60 func newStructExtractor(source reflect.Value) (keyValueExtractor, error) { 61 if source.Kind() != reflect.Struct { 62 return nil, errors.New("expected struct, got: " + source.Type().String()) 63 } 64 return &structExtractor{source}, nil 65 } 66 67 func newMapExtractor(source reflect.Value) (keyValueExtractor, error) { 68 if source.Kind() != reflect.Map { 69 return nil, errors.New("expected map, got: " + source.Type().String()) 70 } else if source.IsNil() { 71 return nil, errors.New("map is nil") 72 } 73 return &mapExtractor{source, source.MapKeys()}, nil 74 } 75 76 func (e *sliceExtractor) getElem(index int, _ interface{}) (interface{}, error) { 77 if index < 0 || index >= e.source.Len() { 78 return nil, errSliceIndexOutOfRange(e.source.Type().Kind() == reflect.Slice, index) 79 } 80 return e.source.Index(index).Interface(), nil 81 } 82 83 func (e *structExtractor) getKey(index int) interface{} { 84 fieldName := e.source.Type().Field(index).Name 85 field, _ := e.source.Type().FieldByName(fieldName) 86 tag := field.Tag.Get("cassandra") 87 if tag != "" { 88 return tag 89 } 90 return strings.ToLower(fieldName) 91 } 92 93 func (e *structExtractor) getElem(_ int, key interface{}) (interface{}, error) { 94 var field reflect.Value 95 if name, ok := key.(string); ok { 96 field = locateFieldByName(e.source, name) 97 } else if index, ok := key.(int); ok { 98 field = locateFieldByIndex(e.source, index) 99 } 100 if !field.IsValid() || !field.CanInterface() { 101 return nil, errStructFieldInvalid(e.source, key) 102 } 103 return field.Interface(), nil 104 } 105 106 func (e *mapExtractor) getKey(index int) interface{} { 107 return e.keys[index].Interface() 108 } 109 110 func (e *mapExtractor) getElem(_ int, key interface{}) (interface{}, error) { 111 keyType := e.source.Type().Key() 112 keyValue := reflect.ValueOf(key) 113 if key == nil { 114 keyValue = reflect.Zero(keyType) 115 } 116 if !keyValue.Type().AssignableTo(keyType) { 117 return nil, errWrongElementType("map key", keyType, keyValue.Type()) 118 } 119 value := e.source.MapIndex(keyValue) 120 if !value.IsValid() || !value.CanInterface() { 121 return nil, nil // key not found 122 } 123 return value.Interface(), nil 124 }