github.com/altipla-consulting/ravendb-go-client@v0.1.3/reflect.go (about)

     1  package ravendb
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  )
     8  
     9  // functionality related to reflection
    10  
    11  func isPtrStruct(t reflect.Type) (reflect.Type, bool) {
    12  	if t.Kind() == reflect.Ptr && t.Elem() != nil && t.Elem().Kind() == reflect.Struct {
    13  		return t, true
    14  	}
    15  	return nil, false
    16  }
    17  
    18  func isPtrMapStringToPtrStruct(tp reflect.Type) (reflect.Type, bool) {
    19  	if tp.Kind() != reflect.Ptr {
    20  		return nil, false
    21  	}
    22  	tp = tp.Elem()
    23  	if tp.Kind() != reflect.Map {
    24  		return nil, false
    25  	}
    26  	if tp.Key().Kind() != reflect.String {
    27  		return nil, false
    28  	}
    29  	return isPtrStruct(tp.Elem())
    30  }
    31  
    32  func isMapStringToPtrStruct(tp reflect.Type) (reflect.Type, bool) {
    33  	if tp.Kind() != reflect.Map {
    34  		return nil, false
    35  	}
    36  	if tp.Key().Kind() != reflect.String {
    37  		return nil, false
    38  	}
    39  	return isPtrStruct(tp.Elem())
    40  }
    41  
    42  // Go port of com.google.common.base.Defaults to make porting Java easier
    43  func getDefaultValueForType(clazz reflect.Type) interface{} {
    44  	rv := reflect.Zero(clazz)
    45  	return rv.Interface()
    46  }
    47  
    48  // GetFullTypeName returns fully qualified (including package) name of the type,
    49  // after traversing pointers.
    50  // e.g. for struct Foo in main package, the type of Foo and *Foo is main.Foo
    51  func getFullTypeName(v interface{}) string {
    52  	rv := reflect.ValueOf(v)
    53  	for rv.Kind() == reflect.Ptr {
    54  		rv = rv.Elem()
    55  	}
    56  	typ := rv.Type()
    57  	return typ.String()
    58  }
    59  
    60  // getShortTypeName returns a short (not including package) name of the type,
    61  // after traversing pointers.
    62  // e.g. for struct Foo, the type of Foo and *Foo is "Foo"
    63  // Note: this emulates Java's operator over-loading to support
    64  // DefaultGetCollectionName.
    65  func getShortTypeNameForEntityOrType(v interface{}) string {
    66  	if typ, ok := v.(reflect.Type); ok {
    67  		return getShortTypeNameForType(typ)
    68  	}
    69  	return getShortTypeNameForEntity(v)
    70  }
    71  
    72  func getShortTypeNameForEntity(v interface{}) string {
    73  	rv := reflect.ValueOf(v)
    74  	for rv.Kind() == reflect.Ptr {
    75  		rv = rv.Elem()
    76  	}
    77  	typ := rv.Type()
    78  	return getShortTypeNameForType(typ)
    79  }
    80  
    81  func getShortTypeNameForType(typ reflect.Type) string {
    82  	// for *Foo and **Foo the name we want to return is Foo
    83  	for typ.Kind() == reflect.Ptr {
    84  		typ = typ.Elem()
    85  	}
    86  	return typ.Name()
    87  }
    88  
    89  // identity property is field of type string with name ID
    90  func getIdentityProperty(typ reflect.Type) string {
    91  	for typ.Kind() == reflect.Ptr {
    92  		typ = typ.Elem()
    93  	}
    94  	if typ.Kind() != reflect.Struct {
    95  		return ""
    96  	}
    97  	field, ok := typ.FieldByName("ID")
    98  	if !ok || field.Type.Kind() != reflect.String {
    99  		return ""
   100  	}
   101  	return "ID"
   102  }
   103  
   104  func isTypePrimitive(t reflect.Type) bool {
   105  	kind := t.Kind()
   106  	switch kind {
   107  	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16,
   108  		reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8,
   109  		reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
   110  		reflect.Float32, reflect.Float64, reflect.String:
   111  		return true
   112  	case reflect.Ptr:
   113  		return false
   114  	// TODO: not all of those we should support
   115  	case reflect.Array, reflect.Interface, reflect.Map, reflect.Slice, reflect.Struct:
   116  		panic("NYI")
   117  	}
   118  	return false
   119  }
   120  
   121  func getStructTypeOfReflectValue(rv reflect.Value) (reflect.Type, bool) {
   122  	if rv.Type().Kind() == reflect.Ptr {
   123  		rv = rv.Elem()
   124  	}
   125  	typ := rv.Type()
   126  	if typ.Kind() == reflect.Struct {
   127  		return typ, true
   128  	}
   129  	return typ, false
   130  }
   131  
   132  func getStructTypeOfValue(v interface{}) (reflect.Type, bool) {
   133  	rv := reflect.ValueOf(v)
   134  	return getStructTypeOfReflectValue(rv)
   135  }
   136  
   137  // if typ is ptr-to-struct, return as is
   138  // if typ is ptr-to-ptr-to-struct, returns ptr-to-struct
   139  // otherwise returns nil
   140  func fixUpStructType(typ reflect.Type) reflect.Type {
   141  	if typ.Kind() != reflect.Ptr {
   142  		return nil
   143  	}
   144  	subtype := typ.Elem()
   145  	if subtype.Kind() == reflect.Struct {
   146  		return typ
   147  	}
   148  	if subtype.Kind() != reflect.Ptr {
   149  		return nil
   150  	}
   151  	if subtype.Elem().Kind() == reflect.Struct {
   152  		return subtype
   153  	}
   154  	return nil
   155  }
   156  
   157  func convertFloat64ToType(v float64, typ reflect.Type) interface{} {
   158  	switch typ.Kind() {
   159  	case reflect.Float32:
   160  		return float32(v)
   161  	case reflect.Float64:
   162  		return v
   163  	case reflect.Int:
   164  		return int(v)
   165  	case reflect.Int8:
   166  		return int8(v)
   167  	case reflect.Int16:
   168  		return int16(v)
   169  	case reflect.Int32:
   170  		return int32(v)
   171  	case reflect.Int64:
   172  		return int64(v)
   173  	case reflect.Uint:
   174  		return uint(v)
   175  	case reflect.Uint8:
   176  		return uint8(v)
   177  	case reflect.Uint16:
   178  		return uint16(v)
   179  	case reflect.Uint32:
   180  		return uint32(v)
   181  	case reflect.Uint64:
   182  		return uint64(v)
   183  	}
   184  	panicIf(true, "don't know how to convert value of type %T to reflect type %s", v, typ.Name())
   185  	return int(0)
   186  }
   187  
   188  func treeToValue(typ reflect.Type, js interface{}) (interface{}, error) {
   189  	// TODO: should also handle primitive types
   190  	switch v := js.(type) {
   191  	case string:
   192  		if typ.Kind() == reflect.String {
   193  			return js, nil
   194  		}
   195  		panicIf(true, "don't know how to convert value of type %T to reflect type %s", js, typ.Name())
   196  	case float64:
   197  		return convertFloat64ToType(v, typ), nil
   198  	case bool:
   199  		panicIf(true, "don't know how to convert value of type %T to reflect type %s", js, typ.Name())
   200  	case []interface{}:
   201  		panicIf(true, "don't know how to convert value of type %T to reflect type %s", js, typ.Name())
   202  	case map[string]interface{}:
   203  		return makeStructFromJSONMap(typ, v)
   204  	}
   205  	panicIf(true, "don't know how to convert value of type %v to reflect type %s", js, typ.Name())
   206  	return nil, fmt.Errorf("don't know how to convert value of type %v to reflect type %s", js, typ.Name())
   207  }
   208  
   209  // get name of struct field for json serialization
   210  // empty string means we should skip this field
   211  func getJSONFieldName(field reflect.StructField) string {
   212  	// skip unexported fields
   213  	if field.PkgPath != "" {
   214  		return ""
   215  	}
   216  
   217  	tag := field.Tag.Get("json")
   218  	// if no tag, use field name
   219  	if tag == "" {
   220  		return field.Name
   221  	}
   222  	// skip if explicitly marked as non-json serializable
   223  	// TODO: write tests for this
   224  	if tag == "-" {
   225  		return ""
   226  	}
   227  	// this could be "name,omitempty" etc.; extract just the name
   228  	if idx := strings.IndexByte(tag, ','); idx != -1 {
   229  		name := tag[:idx-1]
   230  		// if it's sth. like ",omitempty", use field name
   231  		// TODO: write tests for this
   232  		if name == "" {
   233  			return field.Name
   234  		}
   235  		return name
   236  	}
   237  	return tag
   238  }
   239  
   240  // FieldsFor returns names of all fields for the value of a struct type.
   241  // They can be used in e.g. DocumentQuery.SelectFields:
   242  // fields := ravendb.FieldsFor(&MyType{})
   243  // q = q.SelectFields(fields...)
   244  func FieldsFor(s interface{}) []string {
   245  	v := reflect.ValueOf(s)
   246  	// if pointer get the underlying element≤
   247  	for v.Kind() == reflect.Ptr {
   248  		v = v.Elem()
   249  	}
   250  	panicIf(v.Kind() != reflect.Struct, "argument must be struct, we got %T", s)
   251  	t := v.Type()
   252  	var res []string
   253  	for i := 0; i < t.NumField(); i++ {
   254  		if name := getJSONFieldName(t.Field(i)); name != "" {
   255  			res = append(res, name)
   256  		}
   257  	}
   258  	return res
   259  }
   260  
   261  // given js value (most likely as map[string]interface{}) decode into res
   262  func decodeJSONAsStruct(js interface{}, res interface{}) error {
   263  	d, err := jsonMarshal(js)
   264  	if err != nil {
   265  		return err
   266  	}
   267  	return jsonUnmarshal(d, res)
   268  }
   269  
   270  // given a json represented as map and type of a struct
   271  func makeStructFromJSONMap(typ reflect.Type, js map[string]interface{}) (interface{}, error) {
   272  	if typ == reflect.TypeOf(map[string]interface{}{}) {
   273  		return js, nil
   274  	}
   275  	typ2 := fixUpStructType(typ)
   276  	if typ2 == nil {
   277  		return nil, newIllegalArgumentError("typ should be *<type> or *(*<type> but is %s", typ.String())
   278  	}
   279  
   280  	typ = typ2
   281  	// reflect.New() creates a pointer to type. if typ is already a pointer,
   282  	// we undo one level
   283  	if typ.Kind() == reflect.Ptr {
   284  		typ = typ.Elem()
   285  	}
   286  	rvNew := reflect.New(typ)
   287  	d, err := jsonMarshal(js)
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  	v := rvNew.Interface()
   292  	err = jsonUnmarshal(d, v)
   293  	if err != nil {
   294  		return nil, err
   295  	}
   296  	return v, nil
   297  }
   298  
   299  func dbglog(format string, args ...interface{}) string {
   300  	s := fmt.Sprintf(format, args...)
   301  	fmt.Println(s)
   302  	return s
   303  }
   304  
   305  // corresponds to ObjectMapper.convertValue()
   306  // val is coming from JSON, so it can be string, bool, float64, []interface{}
   307  // or map[string]interface{}
   308  // TODO: not sure about nil
   309  // for simple types (int, bool, string) it should be just pass-through
   310  // for structs decode map[string]interface{} => struct using MakeStructFromJSONMap
   311  func convertValue(val interface{}, clazz reflect.Type) (interface{}, error) {
   312  	// TODO: implement every possible type. Need more comprehensive tests
   313  	// to exercise those code paths
   314  	switch clazz.Kind() {
   315  	case reflect.String:
   316  		switch v := val.(type) {
   317  		case string:
   318  			return v, nil
   319  		default:
   320  			panicIf(true, "%s", dbglog("converting of type %T to string NYI", val))
   321  		}
   322  	case reflect.Int:
   323  		switch v := val.(type) {
   324  		case int:
   325  			return v, nil
   326  		case float64:
   327  			res := int(v)
   328  			return res, nil
   329  		default:
   330  			panicIf(true, "%s", dbglog("converting of type %T to reflect.Int NYI", val))
   331  		}
   332  	case reflect.Ptr:
   333  		clazz2 := clazz.Elem()
   334  		switch clazz2.Kind() {
   335  		case reflect.Struct:
   336  			valIn, ok := val.(map[string]interface{})
   337  			if !ok {
   338  				return nil, newRavenError("can't convert value of type '%s' to a struct", val)
   339  			}
   340  			v, err := makeStructFromJSONMap(clazz, valIn)
   341  			return v, err
   342  		default:
   343  			panicIf(true, "%s", dbglog("converting to pointer of '%s' NYI", clazz.Kind().String()))
   344  		}
   345  	default:
   346  		panicIf(true, "%s", dbglog("converting to %s NYI", clazz.Kind().String()))
   347  	}
   348  	return nil, newNotImplementedError("convertValue: NYI")
   349  }
   350  
   351  // m is a single-element map[string]*struct
   352  // returns single map value
   353  func getSingleMapValue(results interface{}) (interface{}, error) {
   354  	m := reflect.ValueOf(results)
   355  	if m.Type().Kind() != reflect.Map {
   356  		return nil, fmt.Errorf("results should be a map[string]*struct, is %s. tp: %s", m.Type().String(), m.Type().String())
   357  	}
   358  	mapKeyType := m.Type().Key()
   359  	if mapKeyType != stringType {
   360  		return nil, fmt.Errorf("results should be a map[string]*struct, is %s. tp: %s", m.Type().String(), m.Type().String())
   361  	}
   362  	mapElemPtrType := m.Type().Elem()
   363  	if mapElemPtrType.Kind() != reflect.Ptr {
   364  		return nil, fmt.Errorf("results should be a map[string]*struct, is %s. tp: %s", m.Type().String(), m.Type().String())
   365  	}
   366  
   367  	mapElemType := mapElemPtrType.Elem()
   368  	if mapElemType.Kind() != reflect.Struct {
   369  		return nil, fmt.Errorf("results should be a map[string]*struct, is %s. tp: %s", m.Type().String(), m.Type().String())
   370  	}
   371  	keys := m.MapKeys()
   372  	if len(keys) == 0 {
   373  		return nil, nil
   374  	}
   375  	if len(keys) != 1 {
   376  		return nil, fmt.Errorf("expected results to have only one element, has %d", len(keys))
   377  	}
   378  	v := m.MapIndex(keys[0])
   379  	return v.Interface(), nil
   380  }
   381  
   382  func checkIsPtrSlice(v interface{}, argName string) error {
   383  	if v == nil {
   384  		return newIllegalArgumentError("%s can't be nil", argName)
   385  	}
   386  	tp := reflect.TypeOf(v)
   387  	if tp.Kind() == reflect.Slice {
   388  		// more specific error message for common error of passing
   389  		// []<type> instead of *[]<type>
   390  		return newIllegalArgumentError("%s can't be of type %T, try *%T", argName, v, v)
   391  	}
   392  	if tp.Kind() != reflect.Ptr {
   393  		return newIllegalArgumentError("%s can't be of type %T", argName, v)
   394  	}
   395  	if tp.Elem().Kind() != reflect.Slice {
   396  		return newIllegalArgumentError("%s can't be of type %T", argName, v)
   397  	}
   398  	return nil
   399  }
   400  
   401  func checkIsPtrPtrStruct(v interface{}, argName string) error {
   402  	if v == nil {
   403  		return newIllegalArgumentError("%s can't be nil", argName)
   404  	}
   405  	tp := reflect.TypeOf(v)
   406  
   407  	if tp.Kind() == reflect.Struct {
   408  		// possibly a common mistake, so try to provide a helpful error message
   409  		typeGot := fmt.Sprintf("%T", v)
   410  		typeExpect := "**" + typeGot
   411  		return newIllegalArgumentError("%s can't be of type %s, try passing %s", argName, typeGot, typeExpect)
   412  	}
   413  
   414  	if tp.Kind() != reflect.Ptr {
   415  		return newIllegalArgumentError("%s can't be of type %T", argName, v)
   416  	}
   417  
   418  	if tp.Elem().Kind() == reflect.Struct {
   419  		// possibly a common mistake, so try to provide a helpful error message
   420  		typeGot := fmt.Sprintf("%T", v)
   421  		typeExpect := "*" + typeGot
   422  		return newIllegalArgumentError("%s can't be of type %s, try passing %s", argName, typeGot, typeExpect)
   423  	}
   424  
   425  	if tp.Elem().Kind() != reflect.Ptr {
   426  		return newIllegalArgumentError("%s can't be of type %T", argName, v)
   427  	}
   428  
   429  	// we only allow pointer to struct
   430  	if tp.Elem().Elem().Kind() == reflect.Struct {
   431  		return nil
   432  	}
   433  	return newIllegalArgumentError("%s can't be of type %T", argName, v)
   434  }