github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/httptransport/route_meta.go (about)

     1  package httptransport
     2  
     3  import (
     4  	"reflect"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/fatih/color"
     9  	"github.com/julienschmidt/httprouter"
    10  
    11  	"github.com/machinefi/w3bstream/pkg/depends/kit/httptransport/httpx"
    12  	"github.com/machinefi/w3bstream/pkg/depends/kit/kit"
    13  )
    14  
    15  type MethodDescriber interface {
    16  	Method() string
    17  }
    18  
    19  type PathDescriber interface {
    20  	Path() string
    21  }
    22  
    23  type BasePathDescriber interface {
    24  	BasePath() string
    25  }
    26  
    27  var pkgHTTPx = reflect.TypeOf(httpx.MethodGet{}).PkgPath()
    28  
    29  func NewOperatorFactoryWithRouteMeta(op kit.Operator, last bool) *OperatorFactoryWithRouteMeta {
    30  	f := kit.NewOperatorFactory(op, last)
    31  
    32  	m := &OperatorFactoryWithRouteMeta{OperatorFactory: f}
    33  
    34  	m.ID = m.Type.Name()
    35  
    36  	if with, ok := op.(MethodDescriber); ok {
    37  		m.Method = with.Method()
    38  	}
    39  
    40  	if m.Type.Kind() == reflect.Struct {
    41  		for i := 0; i < m.Type.NumField(); i++ {
    42  			ft := m.Type.Field(i)
    43  			if !ft.Anonymous || ft.Type.PkgPath() != pkgHTTPx ||
    44  				!strings.HasPrefix(ft.Name, "Method") {
    45  				continue
    46  			}
    47  			// here can parse output operator
    48  			if path, ok := ft.Tag.Lookup("path"); ok {
    49  				vs := strings.Split(path, ",")
    50  				m.Path = vs[0]
    51  
    52  				if len(vs) > 0 {
    53  					for i := range vs {
    54  						switch vs[i] {
    55  						case "deprecated":
    56  							m.Deprecated = true
    57  						}
    58  					}
    59  				}
    60  			}
    61  
    62  			if basePath, ok := ft.Tag.Lookup("basePath"); ok {
    63  				m.BasePath = basePath
    64  			}
    65  
    66  			if summary, ok := ft.Tag.Lookup("summary"); ok {
    67  				m.Summary = summary
    68  			}
    69  
    70  			break
    71  		}
    72  	}
    73  
    74  	if with, ok := op.(BasePathDescriber); ok {
    75  		m.BasePath = with.BasePath()
    76  	}
    77  
    78  	if with, ok := m.Operator.(PathDescriber); ok {
    79  		m.Path = with.Path()
    80  	}
    81  
    82  	return m
    83  }
    84  
    85  type RouteMeta struct {
    86  	ID         string // ID operator name
    87  	Method     string // Method http method implement MethodDescriber
    88  	Path       string // Path in tag `path`
    89  	BasePath   string // BasePath base path
    90  	Summary    string // Summary operator's desc
    91  	Deprecated bool
    92  }
    93  
    94  type OperatorFactoryWithRouteMeta struct {
    95  	*kit.OperatorFactory
    96  	RouteMeta
    97  }
    98  
    99  func NewHttpRouteMeta(route *kit.Route) *HttpRouteMeta {
   100  	metas := make([]*OperatorFactoryWithRouteMeta, len(route.Operators))
   101  
   102  	for i := range route.Operators {
   103  		metas[i] = NewOperatorFactoryWithRouteMeta(
   104  			route.Operators[i],
   105  			i == len(route.Operators)-1,
   106  		)
   107  	}
   108  
   109  	return &HttpRouteMeta{
   110  		Route: route,
   111  		Metas: metas,
   112  	}
   113  }
   114  
   115  type HttpRouteMeta struct {
   116  	Route *kit.Route
   117  	Metas []*OperatorFactoryWithRouteMeta
   118  }
   119  
   120  func (hr *HttpRouteMeta) OperatorNames() string {
   121  	names := make([]string, 0)
   122  
   123  	for _, m := range hr.Metas {
   124  		if m.NoOutput {
   125  			continue
   126  		}
   127  		if m.IsLast {
   128  			names = append(names, color.MagentaString(m.String()))
   129  		} else {
   130  			names = append(names, color.CyanString(m.String()))
   131  		}
   132  	}
   133  
   134  	return strings.Join(names, " ")
   135  }
   136  
   137  func (hr *HttpRouteMeta) Key() string {
   138  	return regexpHttpRouterPath.ReplaceAllString(hr.Path(), "/{$1}") + " " + hr.OperatorNames()
   139  }
   140  
   141  func (hr *HttpRouteMeta) String() string {
   142  	method := hr.Method()
   143  
   144  	return methodColor(method)("%s %s", method[0:3], hr.Key())
   145  }
   146  
   147  func (hr *HttpRouteMeta) Log() {
   148  	method := hr.Method()
   149  
   150  	last := hr.Metas[len(hr.Metas)-1]
   151  
   152  	firstLine := methodColor(method)(
   153  		"%s %s", method[0:3],
   154  		regexpHttpRouterPath.ReplaceAllString(hr.Path(), "/{$1}"))
   155  
   156  	if last.Deprecated {
   157  		firstLine = firstLine + " Deprecated"
   158  	}
   159  
   160  	if last.Summary != "" {
   161  		firstLine = firstLine + " " + last.Summary
   162  	}
   163  
   164  	outputln(firstLine)
   165  	outputln("\t%s", hr.OperatorNames())
   166  }
   167  
   168  var regexpHttpRouterPath = regexp.MustCompile("/:([^/]+)")
   169  
   170  func (hr *HttpRouteMeta) Method() string {
   171  	method := ""
   172  	for _, m := range hr.Metas {
   173  		if m.Method != "" {
   174  			method = m.Method
   175  		}
   176  	}
   177  	return method
   178  }
   179  
   180  func (hr *HttpRouteMeta) Path() string {
   181  	basePath := "/"
   182  	p := ""
   183  
   184  	for _, m := range hr.Metas {
   185  		if m.BasePath != "" {
   186  			basePath = m.BasePath
   187  		}
   188  
   189  		if m.Path != "" {
   190  			p += m.Path
   191  		}
   192  	}
   193  
   194  	return httprouter.CleanPath(basePath + p)
   195  }
   196  
   197  type MetaOperator struct {
   198  	kit.EmptyOperator
   199  	path     string
   200  	basePath string
   201  }
   202  
   203  func BasePath(basePath string) *MetaOperator { return &MetaOperator{basePath: basePath} }
   204  
   205  func Group(path string) *MetaOperator { return &MetaOperator{path: path} }
   206  
   207  func (g *MetaOperator) Path() string { return g.path }
   208  
   209  func (g *MetaOperator) BasePath() string { return g.basePath }