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  }