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 }