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  }