github.com/alimy/mir/v4@v4.1.0/internal/reflex/type.go (about) 1 // Copyright 2022 Michael Li <alimy@gility.net>. All rights reserved. 2 // Use of this source code is governed by Apache License 2.0 that 3 // can be found in the LICENSE file. 4 5 package reflex 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "reflect" 12 "strings" 13 14 "github.com/alimy/mir/v4/internal/utils" 15 ) 16 17 // CheckStruct check struct type is in pkgPath and return other struct type field's 18 // type that contained in type. 19 // st must struct kind 20 func CheckStruct(st reflect.Type, pkgPath string) ([]reflect.Type, error) { 21 var ( 22 innerSts []reflect.Type 23 outSts []reflect.Type 24 ) 25 // just return if type is out pkgPath struct 26 if st.PkgPath() != pkgPath { 27 return []reflect.Type{st}, nil 28 } 29 innerSts = append(innerSts, st) 30 // get all field struct type 31 fields := utils.NewStrSet() 32 fields.Add(st.PkgPath() + "." + st.Name()) 33 for i := 0; i < len(innerSts); i++ { 34 ist := innerSts[i] 35 for i := ist.NumField() - 1; i >= 0; i-- { 36 sf := ist.Field(i) 37 ft := sf.Type 38 ok, wbsts := isWriteableField(ft) 39 if !ok { 40 return nil, fmt.Errorf("yet not support field %v", ft) 41 } 42 for _, t := range wbsts { 43 if t.PkgPath() == pkgPath { 44 fn := t.PkgPath() + "." + ft.Name() 45 if err := fields.Add(fn); err == nil { 46 innerSts = append(innerSts, t) 47 } 48 } else { 49 outSts = append(outSts, t) 50 } 51 } 52 } 53 } 54 // collect all struct type 55 innerSts = append(innerSts, outSts...) 56 return innerSts, nil 57 } 58 59 // CheckExtStruct check external struct type is in pkgPath and return other struct type field's 60 // type that contained in type. 61 // st must struct kind 62 func CheckExtStruct(st reflect.Type, pkgPath string) ([]reflect.Type, error) { 63 var ( 64 innerSts []reflect.Type 65 outSts []reflect.Type 66 ) 67 // just return if type is out pkgPath struct 68 if st.PkgPath() != pkgPath { 69 return []reflect.Type{st}, nil 70 } 71 innerSts = append(innerSts, st) 72 // get all field struct type 73 fields := utils.NewStrSet() 74 fields.Add(st.PkgPath() + "." + st.Name()) 75 for i := 0; i < len(innerSts); i++ { 76 ist := innerSts[i] 77 for i := ist.NumField() - 1; i >= 0; i-- { 78 sf := ist.Field(i) 79 ft := sf.Type 80 ok, wbsts := isWriteableField(ft) 81 if !ok { 82 return nil, fmt.Errorf("yet not support field %v", ft) 83 } 84 for _, t := range wbsts { 85 if t.PkgPath() == pkgPath { 86 fn := t.PkgPath() + "." + ft.Name() 87 if err := fields.Add(fn); err == nil { 88 innerSts = append(innerSts, t) 89 } 90 } else { 91 outSts = append(outSts, t) 92 } 93 } 94 } 95 } 96 return outSts, nil 97 } 98 99 // WriteStruct write struct type to *bytes.Buffer 100 func WriteStruct(buf *bytes.Buffer, t reflect.Type, pkgPath string, imports map[string]string, indent string) (err error) { 101 if buf == nil || t == nil { 102 return errors.New("buf or t of type is nil") 103 } 104 if _, err = buf.WriteString(fmt.Sprintf("type %s struct {\n", t.Name())); err != nil { 105 return 106 } 107 l := t.NumField() 108 for i := 0; i < l; i++ { 109 field := t.Field(i) 110 if err = writeStructField(buf, field, pkgPath, imports, indent); err != nil { 111 return 112 } 113 } 114 _, err = buf.WriteString("}\n") 115 return 116 } 117 118 func writeStructField(buf *bytes.Buffer, sf reflect.StructField, pkgPath string, imports map[string]string, indent string) (err error) { 119 if ok, _ := isWriteableField(sf.Type); !ok { 120 return 121 } 122 buf.WriteString(indent) 123 if !sf.Anonymous { 124 if _, err = buf.WriteString(fmt.Sprintf("%s%s", sf.Name, indent)); err != nil { 125 return 126 } 127 } 128 if err = writeStructFieldType(buf, sf.Type, pkgPath, imports); err != nil { 129 return 130 } 131 if len(sf.Tag) > 0 { 132 _, err = buf.WriteString(fmt.Sprintf("%s`%s`\n", indent, sf.Tag)) 133 } else { 134 _, err = buf.WriteString("\n") 135 } 136 return 137 } 138 139 func writeStructFieldType(buf *bytes.Buffer, t reflect.Type, pkgPath string, imports map[string]string) (err error) { 140 ft := t 141 for ; ft.Kind() == reflect.Pointer; ft = ft.Elem() { 142 if err = buf.WriteByte('*'); err != nil { 143 return 144 } 145 } 146 switch ft.Kind() { 147 case reflect.String, reflect.Bool, 148 reflect.Complex64, reflect.Complex128, 149 reflect.Float32, reflect.Float64, 150 reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 151 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 152 if _, err = buf.WriteString(ft.String()); err != nil { 153 return 154 } 155 case reflect.Array: 156 _, err = buf.WriteString(fmt.Sprintf("[%d]", ft.Len())) 157 if err != nil { 158 return 159 } 160 err = writeStructFieldType(buf, ft.Elem(), pkgPath, imports) 161 if err != nil { 162 return 163 } 164 case reflect.Slice: 165 if _, err = buf.WriteString("[]"); err != nil { 166 return 167 } 168 if err = writeStructFieldType(buf, ft.Elem(), pkgPath, imports); err != nil { 169 return 170 } 171 case reflect.Map: 172 if _, err = buf.WriteString("map["); err != nil { 173 return 174 } 175 if err = writeStructFieldType(buf, ft.Key(), pkgPath, imports); err != nil { 176 return 177 } 178 if _, err = buf.WriteString("]"); err != nil { 179 return 180 } 181 if err = writeStructFieldType(buf, ft.Elem(), pkgPath, imports); err != nil { 182 return 183 } 184 case reflect.Struct: 185 var typn string 186 ftPkgPath := ft.PkgPath() 187 if ftPkgPath == pkgPath { 188 typn = ft.Name() 189 } else if ftPkgPath == "" { 190 typn = ft.String() 191 } else { 192 typn = ft.String() 193 if alias := imports[ftPkgPath]; alias != "" { 194 pkgs := strings.Split(ftPkgPath, "/") 195 typn = strings.Replace(typn, pkgs[len(pkgs)-1]+".", alias+".", 1) 196 } 197 } 198 if _, err = buf.WriteString(typn); err != nil { 199 return 200 } 201 } 202 return 203 } 204 205 func isWriteableField(t reflect.Type) (b bool, sts []reflect.Type) { 206 for t.Kind() == reflect.Pointer { 207 t = t.Elem() 208 } 209 switch t.Kind() { 210 case reflect.String, reflect.Bool, 211 reflect.Complex64, reflect.Complex128, 212 reflect.Float32, reflect.Float64, 213 reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 214 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 215 return true, nil 216 case reflect.Array, reflect.Slice: 217 t = t.Elem() 218 return isWriteableField(t) 219 case reflect.Map: 220 kb, kt := isWriteableMapKey(t.Key()) 221 vb, vt := isWriteableField(t.Elem()) 222 if kt != nil { 223 sts = append(sts, kt) 224 } 225 sts = append(sts, vt...) 226 b = kb && vb 227 return 228 case reflect.Struct: 229 b = len(t.Name()) > 0 230 sts = append(sts, t) 231 return 232 } 233 return false, nil 234 } 235 236 func isWriteableMapKey(t reflect.Type) (bool, reflect.Type) { 237 for t.Kind() == reflect.Pointer { 238 t = t.Elem() 239 } 240 switch t.Kind() { 241 case reflect.String, reflect.Bool, 242 reflect.Complex64, reflect.Complex128, 243 reflect.Float32, reflect.Float64, 244 reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 245 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 246 return true, nil 247 case reflect.Struct: 248 return len(t.Name()) > 0, t 249 } 250 return false, nil 251 }