github.com/alimy/mir/v4@v4.1.0/core/descriptor.go (about) 1 // Copyright 2020 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 core 6 7 import ( 8 "errors" 9 "fmt" 10 "reflect" 11 "sort" 12 "strings" 13 14 "github.com/alimy/mir/v4/internal/utils" 15 ) 16 17 var ( 18 VerInfo = &VersionInfo{ 19 MirVer: "v4.1.0", 20 } 21 ) 22 23 // EngineInfo Engine information 24 type EngineInfo struct { 25 PkgName string 26 ImportAlias string // import alias name 27 } 28 29 // VersionInfo mir version information 30 type VersionInfo struct { 31 MirVer string 32 } 33 34 // FieldDescriptor field Descriptor info 35 type FieldDescriptor struct { 36 Imports map[string]string 37 PkgPath string 38 Host string 39 Path string 40 Queries []string 41 HttpMethods []string 42 IsAnyMethod bool 43 IsFieldChain bool 44 IsUseContext bool 45 IsBindIn bool 46 IsRenderOut bool 47 In reflect.Type 48 Out reflect.Type 49 InOuts []reflect.Type 50 MethodName string 51 Comment string // not support now so always empty 52 } 53 54 // IfaceDescriptor interface Descriptor info 55 type IfaceDescriptor struct { 56 Group string 57 Chain string 58 Imports map[string]string 59 PkgPath string 60 PkgName string 61 TypeName string 62 Comment string // not support now so always empty 63 InOuts []reflect.Type 64 Fields []*FieldDescriptor 65 EngineInfo *EngineInfo 66 VerInfo *VersionInfo 67 WatchCtxDone bool 68 DeclareCoreInterface bool // whether need to declare core interface, default is false 69 } 70 71 // IfaceDescriptors interface Descriptor map {TypeName:*IfaceDescriptor} 72 type IfaceDescriptors map[string]*IfaceDescriptor 73 74 // Descriptors mir's Descriptor map {group: IfaceDescriptors} 75 type Descriptors map[string]IfaceDescriptors 76 77 // Put add a IfaceDescriptor 78 // Notice: if exist same instance by TypeName will override the old one 79 func (d Descriptors) Put(iface *IfaceDescriptor) error { 80 if iface == nil { 81 return errors.New("entry is empty") 82 } 83 key := d.keyFrom(iface.Group) 84 ifaces, exist := d[key] 85 if !exist { 86 ifaces = make(IfaceDescriptors) 87 d[key] = ifaces 88 } 89 if _, exist = ifaces[iface.TypeName]; exist { 90 return fmt.Errorf("had exist entry by name: %s", iface.TypeName) 91 } 92 ifaces[iface.TypeName] = iface 93 return nil 94 } 95 96 // Get get a IfaceDescriptors if exists 97 func (d Descriptors) Get(group string) (IfaceDescriptors, bool) { 98 ifaces, exist := d[d.keyFrom(group)] 99 return ifaces, exist 100 } 101 102 // Exist whether exist *IfaceDescriptor sub-item 103 func (d Descriptors) Exist(iface *IfaceDescriptor) bool { 104 if iface == nil { 105 return false 106 } 107 key := d.keyFrom(iface.Group) 108 ifaces, exist := d[key] 109 if !exist { 110 return false 111 } 112 if _, exist = ifaces[iface.TypeName]; !exist { 113 return false 114 } 115 return true 116 } 117 118 // SortedIfaces return sorted Iface slice 119 func (d IfaceDescriptors) SortedIfaces() []*IfaceDescriptor { 120 keys := make([]string, 0, len(d)) 121 ifaces := make([]*IfaceDescriptor, 0, len(d)) 122 for key := range d { 123 keys = append(keys, key) 124 } 125 sort.Strings(keys) 126 for _, key := range keys { 127 ifaces = append(ifaces, d[key]) 128 } 129 return ifaces 130 } 131 132 // GroupFrom return group name from key 133 func (d Descriptors) GroupFrom(key string) string { 134 return strings.TrimLeft(key, "_") 135 } 136 137 func (d Descriptors) keyFrom(s string) string { 138 return "_" + s 139 } 140 141 // SetPkgName set package name 142 func (d *IfaceDescriptor) SetPkgName(name string) { 143 d.PkgName = name 144 } 145 146 // SetDeclareCoreInterface set declare core interface value 147 func (d *IfaceDescriptor) SetDeclareCoreInterface(isNeed bool) { 148 d.DeclareCoreInterface = isNeed 149 } 150 151 // SetInnerInOuts set inner InOuts for defined 152 func (d *IfaceDescriptor) SetInnerInOuts(inOuts []reflect.Type) { 153 var extSts []reflect.Type 154 for _, t := range inOuts { 155 if t.PkgPath() == d.PkgPath { 156 d.InOuts = append(d.InOuts, t) 157 } else { 158 extSts = append(extSts, t) 159 } 160 } 161 // to set fields pkg name alias map 162 pkgNames := make(map[string]string) 163 for _, t := range extSts { 164 pkgPath := t.PkgPath() 165 if pkgPath == "" { 166 continue 167 } 168 // had import so no need process 169 if _, exist := d.Imports[pkgPath]; exist { 170 continue 171 } 172 // process alias if needed 173 pkgs := strings.Split(pkgPath, "/") 174 pkgName := pkgs[len(pkgs)-1] 175 if pkg, exist := pkgNames[pkgName]; !exist { 176 pkgNames[pkgName] = pkgPath 177 d.Imports[pkgPath] = "" 178 } else { 179 for exist && pkg != pkgPath { 180 pkgName = pkgName + "_m" 181 pkg, exist = pkgNames[pkgName] 182 } 183 pkgNames[pkgName] = pkgPath 184 d.Imports[pkgPath] = pkgName 185 } 186 } 187 d.setFiledImports() 188 } 189 190 func (d *IfaceDescriptor) setFiledImports() { 191 for _, f := range d.Fields { 192 f.Imports = d.Imports 193 } 194 } 195 196 // AllInOuts return all InOuts from Fileds 197 func (d *IfaceDescriptor) AllInOuts() []reflect.Type { 198 tyns := utils.NewStrSet() 199 var inouts []reflect.Type 200 for _, f := range d.Fields { 201 for _, t := range f.InOuts { 202 if !tyns.Exist(t.Name()) { 203 inouts = append(inouts, t) 204 tyns.Add(t.Name()) 205 } 206 } 207 } 208 return inouts 209 } 210 211 // ChainFields return field chains 212 func (d *IfaceDescriptor) ChainFields() (fields []*FieldDescriptor) { 213 for _, f := range d.Fields { 214 if f.IsFieldChain { 215 fields = append(fields, f) 216 } 217 } 218 return 219 } 220 221 // IsUseFieldChain whether use field chain 222 func (d *IfaceDescriptor) IsUseFieldChain() bool { 223 for _, f := range d.Fields { 224 if f.IsFieldChain { 225 return true 226 } 227 } 228 return false 229 } 230 231 // IsUseBinding return whether use binding interface 232 func (d *IfaceDescriptor) IsUseBinding() bool { 233 for _, f := range d.Fields { 234 if f.In != nil { 235 return true 236 } 237 } 238 return false 239 } 240 241 // BindingFields return Binding's fields 242 func (d *IfaceDescriptor) BindingFields() (fields []*FieldDescriptor) { 243 for _, f := range d.Fields { 244 if f.In != nil { 245 fields = append(fields, f) 246 } 247 } 248 return 249 } 250 251 // HttpMethod return http method when f.NotHttpAny() is true 252 func (f *FieldDescriptor) HttpMethod() string { 253 if len(f.HttpMethods) == 1 { 254 return f.HttpMethods[0] 255 } 256 return "" 257 } 258 259 // NotHttpAny not just http any method 260 func (f *FieldDescriptor) NotHttpAny() bool { 261 return !f.IsAnyMethod && len(f.HttpMethods) == 1 262 } 263 264 // JustHttpAny just http any method 265 func (f *FieldDescriptor) JustHttpAny() bool { 266 return f.IsAnyMethod 267 } 268 269 // AnyHttpMethods return methods in HttpMethods 270 func (f *FieldDescriptor) AnyHttpMethods() []string { 271 return f.HttpMethods 272 } 273 274 // HttpMethodArgs return http method as argument like "POST","GET","HEAD" 275 func (f *FieldDescriptor) HttpMethodArgs() string { 276 return utils.QuoteJoin(f.HttpMethods, ",") 277 } 278 279 // JustUseContext whether just use context 280 func (f *FieldDescriptor) JustUseContext() bool { 281 return f.IsUseContext && len(f.InOuts) == 0 282 } 283 284 // OrInOut in or out 285 func (f *FieldDescriptor) OrInOut() bool { 286 return f.In != nil || f.Out != nil 287 } 288 289 // InName return In type name 290 func (f *FieldDescriptor) InName() string { 291 if f.In == nil { 292 return "" 293 } 294 pkgPath := f.In.PkgPath() 295 if pkgPath == f.PkgPath { 296 return f.In.Name() 297 } 298 return f.aliasPkgName(pkgPath) + "." + f.In.Name() 299 } 300 301 // OutName return Out type name 302 func (f *FieldDescriptor) OutName() string { 303 if f.Out == nil { 304 return "" 305 } 306 pkgPath := f.Out.PkgPath() 307 if pkgPath == f.PkgPath { 308 return f.Out.Name() 309 } 310 return f.aliasPkgName(pkgPath) + "." + f.Out.Name() 311 } 312 313 func (f *FieldDescriptor) aliasPkgName(pkgPath string) string { 314 if alias := f.Imports[pkgPath]; alias != "" { 315 return alias 316 } 317 pkgs := strings.Split(pkgPath, "/") 318 return pkgs[len(pkgs)-1] 319 }