github.com/go-generalize/volcago@v1.7.0/generator/testfiles/misc/misc.go (about)

     1  package misc
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sort"
     7  	"strings"
     8  	"time"
     9  
    10  	"cloud.google.com/go/firestore"
    11  	"google.golang.org/genproto/googleapis/type/latlng"
    12  )
    13  
    14  func isReservedType(value reflect.Value) bool {
    15  	switch value.Interface().(type) {
    16  	case time.Time, *time.Time,
    17  		latlng.LatLng, *latlng.LatLng,
    18  		firestore.DocumentRef, *firestore.DocumentRef:
    19  		return true
    20  	}
    21  	return false
    22  }
    23  
    24  func updater(v, param interface{}) []firestore.Update {
    25  	updates := make([]firestore.Update, 0)
    26  	for _, update := range updateBuilder(v, param) {
    27  		updates = append(updates, update)
    28  	}
    29  	sort.Slice(updates, func(i, j int) bool {
    30  		return fmt.Sprint(updates[i].FieldPath) < fmt.Sprint(updates[j].FieldPath)
    31  	})
    32  	return updates
    33  }
    34  
    35  func tagMap(v interface{}) map[string]string {
    36  	rv := reflect.Indirect(reflect.ValueOf(v))
    37  	rt := rv.Type()
    38  	tags := make(map[string]string)
    39  	for i := 0; i < rt.NumField(); i++ {
    40  		ft := rt.Field(i)
    41  		fv := rv.Field(i)
    42  		if ft.Anonymous {
    43  			for key, val := range tagMap(fv.Interface()) {
    44  				if _, ok := tags[key]; ok {
    45  					panic("fields with the same name cannot be used")
    46  				}
    47  				tags[key] = val
    48  			}
    49  			continue
    50  		}
    51  		tag := ft.Name
    52  		if firestoreTag, ok := ft.Tag.Lookup("firestore"); ok {
    53  			tag = strings.Split(firestoreTag, ",")[0]
    54  		}
    55  		switch fv.Kind() {
    56  		case reflect.Ptr:
    57  			ptrType := reflect.PtrTo(fv.Type()).Elem()
    58  			fv = reflect.New(ptrType.Elem())
    59  			fallthrough
    60  		case reflect.Struct:
    61  			if isReservedType(fv) {
    62  				break
    63  			}
    64  			for key, value := range tagMap(fv.Interface()) {
    65  				compositeKey := strings.Join([]string{tag, key}, ".")
    66  				if _, ok := tags[compositeKey]; ok {
    67  					panic("fields with the same name cannot be used")
    68  				}
    69  				compositeValue := strings.Join([]string{tag, value}, ".")
    70  				tags[compositeKey] = compositeValue
    71  			}
    72  			continue
    73  		}
    74  		if _, ok := tags[ft.Name]; ok {
    75  			panic("fields with the same name cannot be used")
    76  		}
    77  		tags[ft.Name] = tag
    78  	}
    79  	return tags
    80  }
    81  
    82  func updateBuilder(v, param interface{}) map[string]firestore.Update {
    83  	rv := reflect.Indirect(reflect.ValueOf(v))
    84  	rt := rv.Type()
    85  	pv := reflect.Indirect(reflect.ValueOf(param))
    86  	pt := pv.Type()
    87  	updateMap := make(map[string]firestore.Update)
    88  	for i := 0; i < rt.NumField(); i++ {
    89  		ft := rt.Field(i)
    90  		fv := rv.Field(i)
    91  
    92  		if ft.Anonymous {
    93  			for key, val := range updateBuilder(fv.Interface(), param) {
    94  				if _, ok := updateMap[key]; ok {
    95  					panic("fields with the same name cannot be used")
    96  				}
    97  				updateMap[key] = val
    98  			}
    99  			continue
   100  		}
   101  
   102  		if _, ok := pt.FieldByName(ft.Name); !ok {
   103  			continue
   104  		}
   105  
   106  		path := ft.Name
   107  		if firestoreTag, ok := ft.Tag.Lookup("firestore"); ok {
   108  			path = strings.Split(firestoreTag, ",")[0]
   109  		}
   110  
   111  		pfv := pv.FieldByName(ft.Name)
   112  
   113  		switch fv.Kind() {
   114  		case reflect.Ptr:
   115  			ptrType := reflect.PtrTo(fv.Type()).Elem()
   116  			fv = reflect.New(ptrType.Elem())
   117  			fallthrough
   118  		case reflect.Struct:
   119  			if isReservedType(fv) {
   120  				break
   121  			}
   122  			if pfv.Interface() == nil {
   123  				continue
   124  			}
   125  			for key, update := range updateBuilder(fv.Interface(), pfv.Interface()) {
   126  				update.FieldPath = append(firestore.FieldPath{path}, update.FieldPath...)
   127  
   128  				fp := make(firestore.FieldPath, len(update.FieldPath))
   129  				copy(fp, update.FieldPath)
   130  
   131  				sp := strings.Split(key, ".")
   132  				fieldKey := strings.Join(append(fp[:len(update.FieldPath)-1], sp[len(sp)-1]), ".")
   133  
   134  				if _, ok := updateMap[fieldKey]; ok {
   135  					panic("fields with the same name cannot be used")
   136  				}
   137  
   138  				updateMap[fieldKey] = update
   139  			}
   140  			continue
   141  		}
   142  
   143  		if _, ok := updateMap[ft.Name]; ok {
   144  			panic("fields with the same name cannot be used")
   145  		}
   146  
   147  		var isValid bool
   148  		switch pfv.Kind() {
   149  		case reflect.Interface, reflect.Ptr:
   150  			if !pfv.IsNil() {
   151  				isValid = true
   152  			}
   153  		default:
   154  			if !pfv.IsZero() {
   155  				isValid = true
   156  			}
   157  		}
   158  
   159  		update := firestore.Update{FieldPath: firestore.FieldPath{path}}
   160  		if isValid {
   161  			update.Value = pfv.Interface()
   162  		}
   163  
   164  		if update.Value != nil {
   165  			updateMap[ft.Name] = update
   166  		}
   167  	}
   168  
   169  	return updateMap
   170  }