Code optimized (#523)

* optimized markdown generator

* optimized markdown generator

* optimized markdown generator

* add more comment

* add comment

* add comment

* add comments for rpc tool

* add comments for model tool

* add comments for model tool

* add comments for model tool

* add comments for config tool

* add comments for config tool

* add comments

* add comments

* add comments

* add comments

* add comment

* remove rpc main head info

* add comment

* optimized

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
This commit is contained in:
kingxt
2021-02-26 16:11:47 +08:00
committed by GitHub
parent ef146cf5ba
commit e6ef1fca12
104 changed files with 651 additions and 375 deletions

View File

@@ -29,7 +29,7 @@ func DocCommand(c *cli.Context) error {
}
if !util.FileExists(dir) {
return errors.New(fmt.Sprintf("dir %s not exsit", dir))
return fmt.Errorf("dir %s not exsit", dir)
}
dir, err := filepath.Abs(dir)

View File

@@ -25,12 +25,13 @@ const (
rightBrace = "}"
)
// GoFormatApi format api file
func GoFormatApi(c *cli.Context) error {
useStdin := c.Bool("stdin")
var be errorx.BatchError
if useStdin {
if err := ApiFormatByStdin(); err != nil {
if err := apiFormatByStdin(); err != nil {
be.Add(err)
}
} else {
@@ -63,7 +64,7 @@ func GoFormatApi(c *cli.Context) error {
return be.Err()
}
func ApiFormatByStdin() error {
func apiFormatByStdin() error {
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return err
@@ -78,6 +79,7 @@ func ApiFormatByStdin() error {
return err
}
// ApiFormatByPath format api from file path
func ApiFormatByPath(apiFilePath string) error {
data, err := ioutil.ReadFile(apiFilePath)
if err != nil {
@@ -135,19 +137,19 @@ func apiFormat(data string) (string, error) {
noCommentLine := util.RemoveComment(line)
if noCommentLine == rightParenthesis || noCommentLine == rightBrace {
tapCount -= 1
tapCount--
}
if tapCount < 0 {
line := strings.TrimSuffix(noCommentLine, rightBrace)
line = strings.TrimSpace(line)
if strings.HasSuffix(line, leftBrace) {
tapCount += 1
tapCount++
}
}
util.WriteIndent(&builder, tapCount)
builder.WriteString(line + ctlutil.NL)
if strings.HasSuffix(noCommentLine, leftParenthesis) || strings.HasSuffix(noCommentLine, leftBrace) {
tapCount += 1
tapCount++
}
preLine = line
}

View File

@@ -25,6 +25,7 @@ const tmpFile = "%s-%d"
var tmpDir = path.Join(os.TempDir(), "goctl")
// GoCommand gen go project files from command line
func GoCommand(c *cli.Context) error {
apiFile := c.String("api")
dir := c.String("dir")
@@ -40,6 +41,7 @@ func GoCommand(c *cli.Context) error {
return DoGenProject(apiFile, dir, namingStyle)
}
// DoGenProject gen go project files with api file
func DoGenProject(apiFile, dir, style string) error {
api, err := parser.Parse(apiFile)
if err != nil {

View File

@@ -39,7 +39,7 @@ func {{.HandlerName}}(ctx *svc.ServiceContext) http.HandlerFunc {
}
`
type Handler struct {
type handlerInfo struct {
ImportPackages string
HandlerName string
RequestType string
@@ -59,7 +59,7 @@ func genHandler(dir string, cfg *config.Config, group spec.Group, route spec.Rou
return err
}
return doGenToFile(dir, handler, cfg, group, route, Handler{
return doGenToFile(dir, handler, cfg, group, route, handlerInfo{
ImportPackages: genHandlerImports(group, route, parentPkg),
HandlerName: handler,
RequestType: util.Title(route.RequestTypeName()),
@@ -71,7 +71,7 @@ func genHandler(dir string, cfg *config.Config, group spec.Group, route spec.Rou
}
func doGenToFile(dir, handler string, cfg *config.Config, group spec.Group,
route spec.Route, handleObj Handler) error {
route spec.Route, handleObj handlerInfo) error {
filename, err := format.FileNamingFormat(cfg.NamingFormat, handler)
if err != nil {
return err

View File

@@ -25,6 +25,7 @@ import (
`
)
// BuildTypes gen types to string
func BuildTypes(types []spec.Type) (string, error) {
var builder strings.Builder
first := true

View File

@@ -12,6 +12,7 @@ import (
"github.com/urfave/cli"
)
// JavaCommand the generate java code command entrance
func JavaCommand(c *cli.Context) error {
apiFile := c.String("api")
dir := c.String("dir")

View File

@@ -7,6 +7,7 @@ import (
"github.com/urfave/cli"
)
// KtCommand the generate kotlin code command entrance
func KtCommand(c *cli.Context) error {
apiFile := c.String("api")
if apiFile == "" {

View File

@@ -27,7 +27,8 @@ service {{.name}}-api {
}
`
func NewService(c *cli.Context) error {
// CreateServiceCommand fast create service
func CreateServiceCommand(c *cli.Context) error {
args := c.Args()
dirName := args.First()
if len(dirName) == 0 {

View File

@@ -7,6 +7,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api"
)
// Api describes syntax for api
type Api struct {
LinePrefix string
Syntax *SyntaxExpr
@@ -21,6 +22,7 @@ type Api struct {
routeM map[string]PlaceHolder
}
// VisitApi implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitApi(ctx *api.ApiContext) interface{} {
var final Api
final.importM = map[string]PlaceHolder{}
@@ -152,6 +154,7 @@ func (v *ApiVisitor) acceptSyntax(root *Api, final *Api) {
}
}
// VisitSpec implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitSpec(ctx *api.SpecContext) interface{} {
var root Api
if ctx.SyntaxLit() != nil {
@@ -178,11 +181,13 @@ func (v *ApiVisitor) VisitSpec(ctx *api.SpecContext) interface{} {
return &root
}
// Format provides a formatter for api command, now nothing to do
func (a *Api) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two Api are equal
func (a *Api) Equal(v interface{}) bool {
if v == nil {
return false

View File

@@ -12,6 +12,7 @@ import (
)
type (
// Parser provides api parsing capabilities
Parser struct {
linePrefix string
debug bool
@@ -19,9 +20,11 @@ type (
antlr.DefaultErrorListener
}
// ParserOption defines an function with argument Parser
ParserOption func(p *Parser)
)
// NewParser creates an instance for Parser
func NewParser(options ...ParserOption) *Parser {
p := &Parser{
log: console.NewColorConsole(),
@@ -425,6 +428,7 @@ func (p *Parser) readContent(filename string) (string, error) {
return string(data), nil
}
// SyntaxError accepts errors and panic it
func (p *Parser) SyntaxError(_ antlr.Recognizer, _ interface{}, line, column int, msg string, _ antlr.RecognitionException) {
str := fmt.Sprintf(`%s line %d:%d %s`, p.linePrefix, line, column, msg)
if p.debug {
@@ -433,12 +437,14 @@ func (p *Parser) SyntaxError(_ antlr.Recognizer, _ interface{}, line, column int
panic(str)
}
// WithParserDebug returns a debug ParserOption
func WithParserDebug() ParserOption {
return func(p *Parser) {
p.debug = true
}
}
// WithParserPrefix returns a prefix ParserOption
func WithParserPrefix(prefix string) ParserOption {
return func(p *Parser) {
p.linePrefix = prefix

View File

@@ -12,11 +12,15 @@ import (
)
type (
// TokenStream defines a token
TokenStream interface {
GetStart() antlr.Token
GetStop() antlr.Token
GetParser() antlr.Parser
}
// ApiVisitor wraps api.BaseApiParserVisitor to call methods which has prefix Visit to
// visit node from the api syntax
ApiVisitor struct {
api.BaseApiParserVisitor
debug bool
@@ -25,8 +29,10 @@ type (
infoFlag bool
}
// VisitorOption defines a function with argument ApiVisitor
VisitorOption func(v *ApiVisitor)
// Spec describes api spec
Spec interface {
Doc() []Expr
Comment() Expr
@@ -34,6 +40,7 @@ type (
Equal(v interface{}) bool
}
// Expr describes ast expression
Expr interface {
Prefix() string
Line() int
@@ -47,6 +54,7 @@ type (
}
)
// NewApiVisitor creates an instance for ApiVisitor
func NewApiVisitor(options ...VisitorOption) *ApiVisitor {
v := &ApiVisitor{
log: console.NewColorConsole(),
@@ -66,12 +74,14 @@ func (v *ApiVisitor) panic(expr Expr, msg string) {
panic(errString)
}
// WithVisitorPrefix returns a VisitorOption wrap with specified prefix
func WithVisitorPrefix(prefix string) VisitorOption {
return func(v *ApiVisitor) {
v.prefix = prefix
}
}
// WithVisitorDebug returns a debug VisitorOption
func WithVisitorDebug() VisitorOption {
return func(v *ApiVisitor) {
v.debug = true
@@ -84,6 +94,7 @@ type defaultExpr struct {
start, stop int
}
// NewTextExpr creates a default instance for Expr
func NewTextExpr(v string) *defaultExpr {
return &defaultExpr{
v: v,
@@ -201,6 +212,7 @@ func (e *defaultExpr) IsNotNil() bool {
return e != nil
}
// EqualDoc compares whether the element literals in two Spec are equal
func EqualDoc(spec1, spec2 Spec) bool {
if spec1 == nil {
return spec2 == nil

View File

@@ -4,6 +4,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api"
)
// ImportExpr defines import syntax for api
type ImportExpr struct {
Import Expr
Value Expr
@@ -11,6 +12,7 @@ type ImportExpr struct {
CommentExpr Expr
}
// VisitImportSpec implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitImportSpec(ctx *api.ImportSpecContext) interface{} {
var list []*ImportExpr
if ctx.ImportLit() != nil {
@@ -25,6 +27,7 @@ func (v *ApiVisitor) VisitImportSpec(ctx *api.ImportSpecContext) interface{} {
return list
}
// VisitImportLit implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitImportLit(ctx *api.ImportLitContext) interface{} {
importToken := v.newExprWithToken(ctx.GetImportToken())
valueExpr := ctx.ImportValue().Accept(v).(Expr)
@@ -38,6 +41,7 @@ func (v *ApiVisitor) VisitImportLit(ctx *api.ImportLitContext) interface{} {
}
}
// VisitImportBlock implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitImportBlock(ctx *api.ImportBlockContext) interface{} {
importToken := v.newExprWithToken(ctx.GetImportToken())
values := ctx.AllImportBlockValue()
@@ -52,6 +56,7 @@ func (v *ApiVisitor) VisitImportBlock(ctx *api.ImportBlockContext) interface{} {
return list
}
// VisitImportBlockValue implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitImportBlockValue(ctx *api.ImportBlockValueContext) interface{} {
value := ctx.ImportValue().Accept(v).(Expr)
return &ImportExpr{
@@ -61,15 +66,18 @@ func (v *ApiVisitor) VisitImportBlockValue(ctx *api.ImportBlockValueContext) int
}
}
// VisitImportValue implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitImportValue(ctx *api.ImportValueContext) interface{} {
return v.newExprWithTerminalNode(ctx.STRING())
}
// Format provides a formatter for api command, now nothing to do
func (i *ImportExpr) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two ImportExpr are equal
func (i *ImportExpr) Equal(v interface{}) bool {
if v == nil {
return false
@@ -87,10 +95,12 @@ func (i *ImportExpr) Equal(v interface{}) bool {
return i.Import.Equal(imp.Import) && i.Value.Equal(imp.Value)
}
// Doc returns the document of ImportExpr, like // some text
func (i *ImportExpr) Doc() []Expr {
return i.DocExpr
}
// Comment returns the comment of ImportExpr, like // some text
func (i *ImportExpr) Comment() Expr {
return i.CommentExpr
}

View File

@@ -4,6 +4,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api"
)
// InfoExpr defines info syntax for api
type InfoExpr struct {
Info Expr
Lp Expr
@@ -11,6 +12,7 @@ type InfoExpr struct {
Kvs []*KvExpr
}
// VisitInfoSpec implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitInfoSpec(ctx *api.InfoSpecContext) interface{} {
var expr InfoExpr
expr.Info = v.newExprWithToken(ctx.GetInfoToken())
@@ -29,11 +31,13 @@ func (v *ApiVisitor) VisitInfoSpec(ctx *api.InfoSpecContext) interface{} {
return &expr
}
// Format provides a formatter for api command, now nothing to do
func (i *InfoExpr) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two InfoExpr are equal
func (i *InfoExpr) Equal(v interface{}) bool {
if v == nil {
return false

View File

@@ -6,6 +6,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api"
)
// KvExpr describes key-value for api
type KvExpr struct {
Key Expr
Value Expr
@@ -13,6 +14,7 @@ type KvExpr struct {
CommentExpr Expr
}
// VisitKvLit implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitKvLit(ctx *api.KvLitContext) interface{} {
var kvExpr KvExpr
kvExpr.Key = v.newExprWithToken(ctx.GetKey())
@@ -48,11 +50,13 @@ func (v *ApiVisitor) VisitKvLit(ctx *api.KvLitContext) interface{} {
return &kvExpr
}
// Format provides a formatter for api command, now nothing to do
func (k *KvExpr) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two KvExpr are equal
func (k *KvExpr) Equal(v interface{}) bool {
if v == nil {
return false
@@ -70,10 +74,12 @@ func (k *KvExpr) Equal(v interface{}) bool {
return k.Key.Equal(kv.Key) && k.Value.Equal(kv.Value)
}
// Doc returns the document of KvExpr, like // some text
func (k *KvExpr) Doc() []Expr {
return k.DocExpr
}
// Comment returns the comment of KvExpr, like // some text
func (k *KvExpr) Comment() Expr {
return k.CommentExpr
}

View File

@@ -1,5 +1,7 @@
package ast
// Holder defines a default instance for PlaceHolder
var Holder PlaceHolder
// PlaceHolder defines an empty struct
type PlaceHolder struct{}

View File

@@ -7,13 +7,16 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api"
)
// Service describes service for api syntax
type Service struct {
AtServer *AtServer
ServiceApi *ServiceApi
}
// KV defines a slice for KvExpr
type KV []*KvExpr
// AtServer describes server metadata for api syntax
type AtServer struct {
AtServerToken Expr
Lp Expr
@@ -21,6 +24,7 @@ type AtServer struct {
Kv KV
}
// ServiceApi describes service ast for api syntax
type ServiceApi struct {
ServiceToken Expr
Name Expr
@@ -29,6 +33,7 @@ type ServiceApi struct {
ServiceRoute []*ServiceRoute
}
// ServiceRoute describes service route ast for api syntax
type ServiceRoute struct {
AtDoc *AtDoc
AtServer *AtServer
@@ -36,6 +41,7 @@ type ServiceRoute struct {
Route *Route
}
// AtDoc describes service comments ast for api syntax
type AtDoc struct {
AtDocToken Expr
Lp Expr
@@ -44,6 +50,7 @@ type AtDoc struct {
Kv []*KvExpr
}
// AtHandler describes service hander ast for api syntax
type AtHandler struct {
AtHandlerToken Expr
Name Expr
@@ -51,6 +58,7 @@ type AtHandler struct {
CommentExpr Expr
}
// Route describes route ast for api syntax
type Route struct {
Method Expr
Path Expr
@@ -61,12 +69,14 @@ type Route struct {
CommentExpr Expr
}
// Body describes request,response body ast for api syntax
type Body struct {
Lp Expr
Rp Expr
Name DataType
}
// VisitServiceSpec implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitServiceSpec(ctx *api.ServiceSpecContext) interface{} {
var serviceSpec Service
if ctx.AtServer() != nil {
@@ -77,6 +87,7 @@ func (v *ApiVisitor) VisitServiceSpec(ctx *api.ServiceSpecContext) interface{} {
return &serviceSpec
}
// VisitAtServer implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitAtServer(ctx *api.AtServerContext) interface{} {
var atServer AtServer
atServer.AtServerToken = v.newExprWithTerminalNode(ctx.ATSERVER())
@@ -90,6 +101,7 @@ func (v *ApiVisitor) VisitAtServer(ctx *api.AtServerContext) interface{} {
return &atServer
}
// VisitServiceApi implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitServiceApi(ctx *api.ServiceApiContext) interface{} {
var serviceApi ServiceApi
serviceApi.ServiceToken = v.newExprWithToken(ctx.GetServiceToken())
@@ -105,6 +117,7 @@ func (v *ApiVisitor) VisitServiceApi(ctx *api.ServiceApiContext) interface{} {
return &serviceApi
}
// VisitServiceRoute implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitServiceRoute(ctx *api.ServiceRouteContext) interface{} {
var serviceRoute ServiceRoute
if ctx.AtDoc() != nil {
@@ -121,6 +134,7 @@ func (v *ApiVisitor) VisitServiceRoute(ctx *api.ServiceRouteContext) interface{}
return &serviceRoute
}
// VisitAtDoc implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitAtDoc(ctx *api.AtDocContext) interface{} {
var atDoc AtDoc
atDoc.AtDocToken = v.newExprWithTerminalNode(ctx.ATDOC())
@@ -150,6 +164,7 @@ func (v *ApiVisitor) VisitAtDoc(ctx *api.AtDocContext) interface{} {
return &atDoc
}
// VisitAtHandler implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitAtHandler(ctx *api.AtHandlerContext) interface{} {
var atHandler AtHandler
astHandlerExpr := v.newExprWithTerminalNode(ctx.ATHANDLER())
@@ -160,6 +175,7 @@ func (v *ApiVisitor) VisitAtHandler(ctx *api.AtHandlerContext) interface{} {
return &atHandler
}
// VisitRoute implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitRoute(ctx *api.RouteContext) interface{} {
var route Route
path := ctx.Path()
@@ -193,6 +209,7 @@ func (v *ApiVisitor) VisitRoute(ctx *api.RouteContext) interface{} {
return &route
}
// VisitBody implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitBody(ctx *api.BodyContext) interface{} {
if ctx.ID() == nil {
return nil
@@ -211,7 +228,7 @@ func (v *ApiVisitor) VisitBody(ctx *api.BodyContext) interface{} {
}
}
// note: forward compatible
// VisitReplybody implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitReplybody(ctx *api.ReplybodyContext) interface{} {
if ctx.DataType() == nil {
return nil
@@ -253,10 +270,13 @@ func (v *ApiVisitor) VisitReplybody(ctx *api.ReplybodyContext) interface{} {
}
}
// Format provides a formatter for api command, now nothing to do
func (b *Body) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two Body are equal
func (b *Body) Equal(v interface{}) bool {
if v == nil {
return false
@@ -278,19 +298,23 @@ func (b *Body) Equal(v interface{}) bool {
return b.Name.Equal(body.Name)
}
// Format provides a formatter for api command, now nothing to do
func (r *Route) Format() error {
// todo
return nil
}
// Doc returns the document of Route, like // some text
func (r *Route) Doc() []Expr {
return r.DocExpr
}
// Comment returns the comment of Route, like // some text
func (r *Route) Comment() Expr {
return r.CommentExpr
}
// Equal compares whether the element literals in two Route are equal
func (r *Route) Equal(v interface{}) bool {
if v == nil {
return false
@@ -330,19 +354,23 @@ func (r *Route) Equal(v interface{}) bool {
return EqualDoc(r, route)
}
// Doc returns the document of AtHandler, like // some text
func (a *AtHandler) Doc() []Expr {
return a.DocExpr
}
// Comment returns the comment of AtHandler, like // some text
func (a *AtHandler) Comment() Expr {
return a.CommentExpr
}
// Format provides a formatter for api command, now nothing to do
func (a *AtHandler) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two AtHandler are equal
func (a *AtHandler) Equal(v interface{}) bool {
if v == nil {
return false
@@ -364,11 +392,13 @@ func (a *AtHandler) Equal(v interface{}) bool {
return EqualDoc(a, atHandler)
}
// Format provides a formatter for api command, now nothing to do
func (a *AtDoc) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two AtDoc are equal
func (a *AtDoc) Equal(v interface{}) bool {
if v == nil {
return false
@@ -419,11 +449,13 @@ func (a *AtDoc) Equal(v interface{}) bool {
return true
}
// Format provides a formatter for api command, now nothing to do
func (a *AtServer) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two AtServer are equal
func (a *AtServer) Equal(v interface{}) bool {
if v == nil {
return false
@@ -471,6 +503,7 @@ func (a *AtServer) Equal(v interface{}) bool {
return true
}
// Equal compares whether the element literals in two ServiceRoute are equal
func (s *ServiceRoute) Equal(v interface{}) bool {
if v == nil {
return false
@@ -500,11 +533,13 @@ func (s *ServiceRoute) Equal(v interface{}) bool {
return s.Route.Equal(sr.Route)
}
// Format provides a formatter for api command, now nothing to do
func (s *ServiceRoute) Format() error {
// todo
return nil
}
// GetHandler returns handler name of api route
func (s *ServiceRoute) GetHandler() Expr {
if s.AtHandler != nil {
return s.AtHandler.Name
@@ -513,11 +548,13 @@ func (s *ServiceRoute) GetHandler() Expr {
return s.AtServer.Kv.Get("handler")
}
// Format provides a formatter for api command, now nothing to do
func (a *ServiceApi) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two ServiceApi are equal
func (a *ServiceApi) Equal(v interface{}) bool {
if v == nil {
return false
@@ -569,11 +606,13 @@ func (a *ServiceApi) Equal(v interface{}) bool {
return true
}
// Format provides a formatter for api command, now nothing to do
func (s *Service) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two Service are equal
func (s *Service) Equal(v interface{}) bool {
if v == nil {
return false
@@ -593,6 +632,7 @@ func (s *Service) Equal(v interface{}) bool {
return s.ServiceApi.Equal(service.ServiceApi)
}
// Get returns the tergate KV by specified key
func (kv KV) Get(key string) Expr {
for _, each := range kv {
if each.Key.Text() == key {

View File

@@ -4,6 +4,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api"
)
// SyntaxExpr describes syntax for api
type SyntaxExpr struct {
Syntax Expr
Assign Expr
@@ -12,6 +13,7 @@ type SyntaxExpr struct {
CommentExpr Expr
}
// VisitSyntaxLit implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitSyntaxLit(ctx *api.SyntaxLitContext) interface{} {
syntax := v.newExprWithToken(ctx.GetSyntaxToken())
assign := v.newExprWithToken(ctx.GetAssign())
@@ -25,11 +27,13 @@ func (v *ApiVisitor) VisitSyntaxLit(ctx *api.SyntaxLitContext) interface{} {
}
}
// Format provides a formatter for api command, now nothing to do
func (s *SyntaxExpr) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two SyntaxExpr are equal
func (s *SyntaxExpr) Equal(v interface{}) bool {
if v == nil {
return false
@@ -49,10 +53,12 @@ func (s *SyntaxExpr) Equal(v interface{}) bool {
s.Version.Equal(syntax.Version)
}
// Doc returns the document of SyntaxExpr, like // some text
func (s *SyntaxExpr) Doc() []Expr {
return s.DocExpr
}
// Comment returns the comment of SyntaxExpr, like // some text
func (s *SyntaxExpr) Comment() Expr {
return s.CommentExpr
}

View File

@@ -9,13 +9,15 @@ import (
)
type (
// TypeAlias TypeStruct
// TypeExpr describes an expression for TypeAlias and TypeStruct
TypeExpr interface {
Doc() []Expr
Format() error
Equal(v interface{}) bool
NameExpr() Expr
}
// TypeAlias describes alias ast for api syatax
TypeAlias struct {
Name Expr
Assign Expr
@@ -24,6 +26,7 @@ type (
CommentExpr Expr
}
// TypeStruct describes structure ast for api syatax
TypeStruct struct {
Name Expr
Struct Expr
@@ -33,6 +36,7 @@ type (
Fields []*TypeField
}
// TypeField describes field ast for api syntax
TypeField struct {
IsAnonymous bool
// Name is nil if IsAnonymous
@@ -43,6 +47,7 @@ type (
CommentExpr Expr
}
// DataType describes datatype for api syntax, the default implementation expressions are
// Literal, Interface, Map, Array, Time, Pointer
DataType interface {
Expr() Expr
@@ -51,15 +56,18 @@ type (
IsNotNil() bool
}
// int, bool, Foo,...
// Literal describes the basic types of golang, non-reference types,
// such as int, bool, Foo,...
Literal struct {
Literal Expr
}
// Interface describes the interface type of golang,Its fixed value is interface{}
Interface struct {
Literal Expr
}
// Map describes the map ast for api syntax
Map struct {
MapExpr Expr
Map Expr
@@ -69,6 +77,7 @@ type (
Value DataType
}
// Array describes the slice ast for api syntax
Array struct {
ArrayExpr Expr
LBrack Expr
@@ -76,10 +85,12 @@ type (
Literal DataType
}
// Time describes the time ast for api syntax
Time struct {
Literal Expr
}
// Pointer describes the pointer ast for api syntax
Pointer struct {
PointerExpr Expr
Star Expr
@@ -87,6 +98,7 @@ type (
}
)
// VisitTypeSpec implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitTypeSpec(ctx *api.TypeSpecContext) interface{} {
if ctx.TypeLit() != nil {
return []TypeExpr{ctx.TypeLit().Accept(v).(TypeExpr)}
@@ -94,6 +106,7 @@ func (v *ApiVisitor) VisitTypeSpec(ctx *api.TypeSpecContext) interface{} {
return ctx.TypeBlock().Accept(v)
}
// VisitTypeLit implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitTypeLit(ctx *api.TypeLitContext) interface{} {
typeLit := ctx.TypeLitBody().Accept(v)
alias, ok := typeLit.(*TypeAlias)
@@ -109,6 +122,7 @@ func (v *ApiVisitor) VisitTypeLit(ctx *api.TypeLitContext) interface{} {
return typeLit
}
// VisitTypeBlock implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitTypeBlock(ctx *api.TypeBlockContext) interface{} {
list := ctx.AllTypeBlockBody()
var types []TypeExpr
@@ -119,6 +133,7 @@ func (v *ApiVisitor) VisitTypeBlock(ctx *api.TypeBlockContext) interface{} {
return types
}
// VisitTypeLitBody implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitTypeLitBody(ctx *api.TypeLitBodyContext) interface{} {
if ctx.TypeAlias() != nil {
return ctx.TypeAlias().Accept(v)
@@ -126,6 +141,7 @@ func (v *ApiVisitor) VisitTypeLitBody(ctx *api.TypeLitBodyContext) interface{} {
return ctx.TypeStruct().Accept(v)
}
// VisitTypeBlockBody implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitTypeBlockBody(ctx *api.TypeBlockBodyContext) interface{} {
if ctx.TypeBlockAlias() != nil {
return ctx.TypeBlockAlias().Accept(v).(*TypeAlias)
@@ -133,6 +149,7 @@ func (v *ApiVisitor) VisitTypeBlockBody(ctx *api.TypeBlockBodyContext) interface
return ctx.TypeBlockStruct().Accept(v).(*TypeStruct)
}
// VisitTypeStruct implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitTypeStruct(ctx *api.TypeStructContext) interface{} {
var st TypeStruct
st.Name = v.newExprWithToken(ctx.GetStructName())
@@ -168,6 +185,7 @@ func (v *ApiVisitor) VisitTypeStruct(ctx *api.TypeStructContext) interface{} {
return &st
}
// VisitTypeBlockStruct implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitTypeBlockStruct(ctx *api.TypeBlockStructContext) interface{} {
var st TypeStruct
st.Name = v.newExprWithToken(ctx.GetStructName())
@@ -200,6 +218,7 @@ func (v *ApiVisitor) VisitTypeBlockStruct(ctx *api.TypeBlockStructContext) inter
return &st
}
// VisitTypeBlockAlias implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitTypeBlockAlias(ctx *api.TypeBlockAliasContext) interface{} {
var alias TypeAlias
alias.Name = v.newExprWithToken(ctx.GetAlias())
@@ -212,6 +231,7 @@ func (v *ApiVisitor) VisitTypeBlockAlias(ctx *api.TypeBlockAliasContext) interfa
return &alias
}
// VisitTypeAlias implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitTypeAlias(ctx *api.TypeAliasContext) interface{} {
var alias TypeAlias
alias.Name = v.newExprWithToken(ctx.GetAlias())
@@ -224,6 +244,7 @@ func (v *ApiVisitor) VisitTypeAlias(ctx *api.TypeAliasContext) interface{} {
return &alias
}
// VisitField implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitField(ctx *api.FieldContext) interface{} {
iAnonymousFiled := ctx.AnonymousFiled()
iNormalFieldContext := ctx.NormalField()
@@ -236,6 +257,7 @@ func (v *ApiVisitor) VisitField(ctx *api.FieldContext) interface{} {
return nil
}
// VisitNormalField implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitNormalField(ctx *api.NormalFieldContext) interface{} {
var field TypeField
field.Name = v.newExprWithToken(ctx.GetFieldName())
@@ -259,6 +281,7 @@ func (v *ApiVisitor) VisitNormalField(ctx *api.NormalFieldContext) interface{} {
return &field
}
// VisitAnonymousFiled implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitAnonymousFiled(ctx *api.AnonymousFiledContext) interface{} {
start := ctx.GetStart()
stop := ctx.GetStop()
@@ -282,6 +305,7 @@ func (v *ApiVisitor) VisitAnonymousFiled(ctx *api.AnonymousFiledContext) interfa
return &field
}
// VisitDataType implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitDataType(ctx *api.DataTypeContext) interface{} {
if ctx.ID() != nil {
idExpr := v.newExprWithTerminalNode(ctx.ID())
@@ -310,6 +334,7 @@ func (v *ApiVisitor) VisitDataType(ctx *api.DataTypeContext) interface{} {
return ctx.TypeStruct().Accept(v)
}
// VisitPointerType implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitPointerType(ctx *api.PointerTypeContext) interface{} {
nameExpr := v.newExprWithTerminalNode(ctx.ID())
v.exportCheck(nameExpr)
@@ -320,6 +345,7 @@ func (v *ApiVisitor) VisitPointerType(ctx *api.PointerTypeContext) interface{} {
}
}
// VisitMapType implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitMapType(ctx *api.MapTypeContext) interface{} {
return &Map{
MapExpr: v.newExprWithText(ctx.GetText(), ctx.GetMapToken().GetLine(), ctx.GetMapToken().GetColumn(),
@@ -332,6 +358,7 @@ func (v *ApiVisitor) VisitMapType(ctx *api.MapTypeContext) interface{} {
}
}
// VisitArrayType implements from api.BaseApiParserVisitor
func (v *ApiVisitor) VisitArrayType(ctx *api.ArrayTypeContext) interface{} {
return &Array{
ArrayExpr: v.newExprWithText(ctx.GetText(), ctx.GetLbrack().GetLine(), ctx.GetLbrack().GetColumn(), ctx.GetLbrack().GetStart(), ctx.DataType().GetStop().GetStop()),
@@ -341,22 +368,27 @@ func (v *ApiVisitor) VisitArrayType(ctx *api.ArrayTypeContext) interface{} {
}
}
// NameExpr returns the expression string of TypeAlias
func (a *TypeAlias) NameExpr() Expr {
return a.Name
}
// Doc returns the document of TypeAlias, like // some text
func (a *TypeAlias) Doc() []Expr {
return a.DocExpr
}
// Comment returns the comment of TypeAlias, like // some text
func (a *TypeAlias) Comment() Expr {
return a.CommentExpr
}
// Format provides a formatter for api command, now nothing to do
func (a *TypeAlias) Format() error {
return nil
}
// Equal compares whether the element literals in two TypeAlias are equal
func (a *TypeAlias) Equal(v interface{}) bool {
if v == nil {
return false
@@ -378,15 +410,18 @@ func (a *TypeAlias) Equal(v interface{}) bool {
return EqualDoc(a, alias)
}
// Expr returns the expression string of Literal
func (l *Literal) Expr() Expr {
return l.Literal
}
// Format provides a formatter for api command, now nothing to do
func (l *Literal) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two Literal are equal
func (l *Literal) Equal(dt DataType) bool {
if dt == nil {
return false
@@ -400,19 +435,23 @@ func (l *Literal) Equal(dt DataType) bool {
return l.Literal.Equal(v.Literal)
}
// IsNotNil returns whether the instance is nil or not
func (l *Literal) IsNotNil() bool {
return l != nil
}
// Expr returns the expression string of Interface
func (i *Interface) Expr() Expr {
return i.Literal
}
// Format provides a formatter for api command, now nothing to do
func (i *Interface) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two Interface are equal
func (i *Interface) Equal(dt DataType) bool {
if dt == nil {
return false
@@ -426,19 +465,23 @@ func (i *Interface) Equal(dt DataType) bool {
return i.Literal.Equal(v.Literal)
}
// IsNotNil returns whether the instance is nil or not
func (i *Interface) IsNotNil() bool {
return i != nil
}
// Expr returns the expression string of Map
func (m *Map) Expr() Expr {
return m.MapExpr
}
// Format provides a formatter for api command, now nothing to do
func (m *Map) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two Map are equal
func (m *Map) Equal(dt DataType) bool {
if dt == nil {
return false
@@ -464,19 +507,23 @@ func (m *Map) Equal(dt DataType) bool {
return m.Map.Equal(v.Map)
}
// IsNotNil returns whether the instance is nil or not
func (m *Map) IsNotNil() bool {
return m != nil
}
// Expr returns the expression string of Array
func (a *Array) Expr() Expr {
return a.ArrayExpr
}
// Format provides a formatter for api command, now nothing to do
func (a *Array) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two Array are equal
func (a *Array) Equal(dt DataType) bool {
if dt == nil {
return false
@@ -494,19 +541,23 @@ func (a *Array) Equal(dt DataType) bool {
return a.Literal.Equal(v.Literal)
}
// IsNotNil returns whether the instance is nil or not
func (a *Array) IsNotNil() bool {
return a != nil
}
// Expr returns the expression string of Time
func (t *Time) Expr() Expr {
return t.Literal
}
// Format provides a formatter for api command, now nothing to do
func (t *Time) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two Time are equal
func (t *Time) Equal(dt DataType) bool {
if dt == nil {
return false
@@ -520,18 +571,22 @@ func (t *Time) Equal(dt DataType) bool {
return t.Literal.Equal(v.Literal)
}
// IsNotNil returns whether the instance is nil or not
func (t *Time) IsNotNil() bool {
return t != nil
}
// Expr returns the expression string of Pointer
func (p *Pointer) Expr() Expr {
return p.PointerExpr
}
// Format provides a formatter for api command, now nothing to do
func (p *Pointer) Format() error {
return nil
}
// Equal compares whether the element literals in two Pointer are equal
func (p *Pointer) Equal(dt DataType) bool {
if dt == nil {
return false
@@ -553,14 +608,17 @@ func (p *Pointer) Equal(dt DataType) bool {
return p.Name.Equal(v.Name)
}
// IsNotNil returns whether the instance is nil or not
func (p *Pointer) IsNotNil() bool {
return p != nil
}
// NameExpr returns the expression string of TypeStruct
func (s *TypeStruct) NameExpr() Expr {
return s.Name
}
// Equal compares whether the element literals in two TypeStruct are equal
func (s *TypeStruct) Equal(dt interface{}) bool {
if dt == nil {
return false
@@ -621,15 +679,18 @@ func (s *TypeStruct) Equal(dt interface{}) bool {
return true
}
// Doc returns the document of TypeStruct, like // some text
func (s *TypeStruct) Doc() []Expr {
return s.DocExpr
}
// Format provides a formatter for api command, now nothing to do
func (s *TypeStruct) Format() error {
// todo
return nil
}
// Equal compares whether the element literals in two TypeField are equal
func (t *TypeField) Equal(v interface{}) bool {
if v == nil {
return false
@@ -663,14 +724,17 @@ func (t *TypeField) Equal(v interface{}) bool {
return EqualDoc(t, f)
}
// Doc returns the document of TypeField, like // some text
func (t *TypeField) Doc() []Expr {
return t.DocExpr
}
// Comment returns the comment of TypeField, like // some text
func (t *TypeField) Comment() Expr {
return t.CommentExpr
}
// Format provides a formatter for api command, now nothing to do
func (t *TypeField) Format() error {
// todo
return nil

View File

@@ -4931,7 +4931,7 @@ func (p *ApiParserParser) Route() (localctx IRouteContext) {
}()
p.EnterOuterAlt(localctx, 1)
checkHttpMethod(p)
checkHTTPMethod(p)
{
p.SetState(291)

View File

@@ -74,7 +74,7 @@ func checkKeyValue(p *ApiParserParser) {
setCurrentTokenText(p, v)
}
func checkHttpMethod(p *ApiParserParser) {
func checkHTTPMethod(p *ApiParserParser) {
method := getCurrentTokenText(p)
uppler := strings.ToUpper(method)
switch uppler {
@@ -107,11 +107,13 @@ func checkKey(p *ApiParserParser) {
}
}
// IsBasicType returns true if the input argument is basic golang type
func IsBasicType(text string) bool {
_, ok := kind[text]
return ok
}
// IsGolangKeyWord returns true if input argument is golang keyword, but it will be ignored which in excepts
func IsGolangKeyWord(text string, excepts ...string) bool {
for _, each := range excepts {
if text == each {
@@ -171,6 +173,7 @@ func isNormal(p *ApiParserParser) bool {
return len(list) > 1
}
// MatchTag returns a Boolean value, which returns true if it does matched, otherwise returns fase
func MatchTag(v string) bool {
return matchRegex(v, tagRegex)
}

View File

@@ -12,7 +12,7 @@ import (
)
var (
normalApi = `
normalAPI = `
syntax="v1"
info (
@@ -32,7 +32,7 @@ var (
post /foo (Foo) returns ([]int)
}
`
missDeclarationApi = `
missDeclarationAPI = `
@server(
foo: bar
)
@@ -43,7 +43,7 @@ var (
}
`
missDeclarationInArrayApi = `
missDeclarationInArrayAPI = `
@server(
foo: bar
)
@@ -54,7 +54,7 @@ var (
}
`
missDeclarationInArrayApi2 = `
missDeclarationInArrayAPI2 = `
@server(
foo: bar
)
@@ -65,7 +65,7 @@ var (
}
`
nestedApiImport = `
nestedAPIImport = `
import "foo.api"
`
@@ -99,27 +99,27 @@ var (
)
func TestApiParser(t *testing.T) {
t.Run("missDeclarationApi", func(t *testing.T) {
_, err := parser.ParseContent(missDeclarationApi)
t.Run("missDeclarationAPI", func(t *testing.T) {
_, err := parser.ParseContent(missDeclarationAPI)
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("missDeclarationApi", func(t *testing.T) {
_, err := parser.ParseContent(missDeclarationInArrayApi)
t.Run("missDeclarationAPI", func(t *testing.T) {
_, err := parser.ParseContent(missDeclarationInArrayAPI)
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("missDeclarationApi", func(t *testing.T) {
_, err := parser.ParseContent(missDeclarationInArrayApi2)
t.Run("missDeclarationAPI", func(t *testing.T) {
_, err := parser.ParseContent(missDeclarationInArrayAPI2)
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("nestedImport", func(t *testing.T) {
file := filepath.Join(t.TempDir(), "foo.api")
err := ioutil.WriteFile(file, []byte(nestedApiImport), os.ModePerm)
err := ioutil.WriteFile(file, []byte(nestedAPIImport), os.ModePerm)
if err != nil {
return
}
@@ -275,7 +275,7 @@ func TestApiParser(t *testing.T) {
})
t.Run("normal", func(t *testing.T) {
v, err := parser.ParseContent(normalApi)
v, err := parser.ParseContent(normalAPI)
assert.Nil(t, err)
body := &ast.Body{
Lp: ast.NewTextExpr("("),

View File

@@ -15,6 +15,7 @@ type parser struct {
spec *spec.ApiSpec
}
// Parse parses the api file
func Parse(filename string) (*spec.ApiSpec, error) {
astParser := ast.NewParser(ast.WithParserPrefix(filepath.Base(filename)))
ast, err := astParser.Parse(filename)
@@ -32,6 +33,7 @@ func Parse(filename string) (*spec.ApiSpec, error) {
return spec, nil
}
// ParseContent parses the api content
func ParseContent(content string) (*spec.ApiSpec, error) {
astParser := ast.NewParser()
ast, err := astParser.ParseContent(content)

View File

@@ -16,6 +16,7 @@ const (
var definedKeys = []string{bodyTagKey, formTagKey, "path"}
// Routes returns all routes in api service
func (s Service) Routes() []Route {
var result []Route
for _, group := range s.Groups {
@@ -24,6 +25,7 @@ func (s Service) Routes() []Route {
return result
}
// Tags retuens all tags in Member
func (m Member) Tags() []*Tag {
tags, err := Parse(m.Tag)
if err != nil {
@@ -33,6 +35,7 @@ func (m Member) Tags() []*Tag {
return tags.Tags()
}
// IsOptional returns true if tag is optional
func (m Member) IsOptional() bool {
if !m.IsBodyMember() {
return false
@@ -49,6 +52,7 @@ func (m Member) IsOptional() bool {
return false
}
// IsOmitEmpty returns true if tag contains omitempty
func (m Member) IsOmitEmpty() bool {
if !m.IsBodyMember() {
return false
@@ -65,22 +69,7 @@ func (m Member) IsOmitEmpty() bool {
return false
}
func (m Member) IsOmitempty() bool {
if !m.IsBodyMember() {
return false
}
tag := m.Tags()
for _, item := range tag {
if item.Key == bodyTagKey {
if stringx.Contains(item.Options, "omitempty") {
return true
}
}
}
return false
}
// GetPropertyName returns json tag value
func (m Member) GetPropertyName() (string, error) {
tags := m.Tags()
for _, tag := range tags {
@@ -95,10 +84,12 @@ func (m Member) GetPropertyName() (string, error) {
return "", errors.New("json property name not exist, member: " + m.Name)
}
// GetComment returns comment value of Member
func (m Member) GetComment() string {
return strings.TrimSpace(m.Comment)
}
// IsBodyMember returns true if contains json tag
func (m Member) IsBodyMember() bool {
if m.IsInline {
return true
@@ -113,6 +104,7 @@ func (m Member) IsBodyMember() bool {
return false
}
// IsFormMember returns true if contains form tag
func (m Member) IsFormMember() bool {
if m.IsInline {
return false
@@ -127,6 +119,7 @@ func (m Member) IsFormMember() bool {
return false
}
// GetBodyMembers returns all json fields
func (t DefineStruct) GetBodyMembers() []Member {
var result []Member
for _, member := range t.Members {
@@ -137,6 +130,7 @@ func (t DefineStruct) GetBodyMembers() []Member {
return result
}
// GetFormMembers returns all form fields
func (t DefineStruct) GetFormMembers() []Member {
var result []Member
for _, member := range t.Members {
@@ -147,6 +141,7 @@ func (t DefineStruct) GetFormMembers() []Member {
return result
}
// GetNonBodyMembers retruns all have no tag fields
func (t DefineStruct) GetNonBodyMembers() []Member {
var result []Member
for _, member := range t.Members {
@@ -157,6 +152,7 @@ func (t DefineStruct) GetNonBodyMembers() []Member {
return result
}
// JoinedDoc joins comments and summary value in AtDoc
func (r Route) JoinedDoc() string {
doc := r.AtDoc.Text
if r.AtDoc.Properties != nil {
@@ -166,6 +162,7 @@ func (r Route) JoinedDoc() string {
return strings.TrimSpace(doc)
}
// GetAnnotation returns the value by specified key
func (r Route) GetAnnotation(key string) string {
if r.Annotation.Properties == nil {
return ""
@@ -174,6 +171,7 @@ func (r Route) GetAnnotation(key string) string {
return r.Annotation.Properties[key]
}
// GetAnnotation returns the value by specified key
func (g Group) GetAnnotation(key string) string {
if g.Annotation.Properties == nil {
return ""
@@ -182,6 +180,7 @@ func (g Group) GetAnnotation(key string) string {
return g.Annotation.Properties[key]
}
// ResponseTypeName returns response type name of route
func (r Route) ResponseTypeName() string {
if r.ResponseType == nil {
return ""
@@ -190,6 +189,7 @@ func (r Route) ResponseTypeName() string {
return r.ResponseType.Name()
}
// RequestTypeName returns request type name of route
func (r Route) RequestTypeName() string {
if r.RequestType == nil {
return ""

View File

@@ -1,25 +1,31 @@
package spec
// Name returns a basic string, such as int32,int64
func (t PrimitiveType) Name() string {
return t.RawName
}
// Name returns a structure string, such as User
func (t DefineStruct) Name() string {
return t.RawName
}
// Name returns a map string, such as map[string]int
func (t MapType) Name() string {
return t.RawName
}
// Name returns a slice string, such as []int
func (t ArrayType) Name() string {
return t.RawName
}
// Name returns a pointer string, such as *User
func (t PointerType) Name() string {
return t.RawName
}
// Name returns a interface string, Its fixed value is interface{}
func (t InterfaceType) Name() string {
return t.RawName
}

View File

@@ -1,16 +1,20 @@
package spec
type (
// Doc describes document
Doc []string
// Annotation defines key-value
Annotation struct {
Properties map[string]string
}
// ApiSyntax describes the syntax grammar
ApiSyntax struct {
Version string
}
// ApiSpec describes a api file
ApiSpec struct {
Info Info
Syntax ApiSyntax
@@ -19,15 +23,18 @@ type (
Service Service
}
// Import describes api import
Import struct {
Value string
}
// Group defines a set of routing information
Group struct {
Annotation Annotation
Routes []Route
}
// Info describes info grammar block
Info struct {
// Deprecated: use Properties instead
Title string
@@ -42,6 +49,7 @@ type (
Properties map[string]string
}
// Member describes the field of a structure
Member struct {
Name string
// 数据类型字面值string、map[int]string、[]int64、[]*User
@@ -53,6 +61,7 @@ type (
IsInline bool
}
// Route describes api route
Route struct {
Annotation Annotation
Method string
@@ -64,26 +73,30 @@ type (
AtDoc AtDoc
}
// Service describes api service
Service struct {
Name string
Groups []Group
}
// Type defines api type
Type interface {
Name() string
}
// DefineStruct describes api structure
DefineStruct struct {
RawName string
Members []Member
Docs Doc
}
// 系统预设基本数据类型 bool int32 int64 float32
// PrimitiveType describes the basic golang type, such as bool,int32,int64, ...
PrimitiveType struct {
RawName string
}
// MapType describes a map for api
MapType struct {
RawName string
// only support the PrimitiveType
@@ -97,20 +110,24 @@ type (
Value Type
}
// ArrayType describes a slice for api
ArrayType struct {
RawName string
Value Type
}
// InterfaceType describes a interface for api
InterfaceType struct {
RawName string
}
// PointerType describes a pointer for api
PointerType struct {
RawName string
Type Type
}
// AtDoc describes a metadata for api grammar: @doc(...)
AtDoc struct {
Properties map[string]string
Text string

View File

@@ -10,6 +10,7 @@ import (
var errTagNotExist = errors.New("tag does not exist")
type (
// Tag defines a tag for structure filed
Tag struct {
// Key is the tag key, such as json, xml, etc..
// i.e: `json:"foo,omitempty". Here key is: "json"
@@ -24,11 +25,13 @@ type (
Options []string
}
// Tags defines a slice for Tag
Tags struct {
tags []*Tag
}
)
// Parse converts tag string into Tag
func Parse(tag string) (*Tags, error) {
tag = strings.TrimPrefix(tag, "`")
tag = strings.TrimSuffix(tag, "`")
@@ -44,6 +47,7 @@ func Parse(tag string) (*Tags, error) {
return &result, nil
}
// Get gets tag value by specified key
func (t *Tags) Get(key string) (*Tag, error) {
for _, tag := range t.tags {
if tag.Key == key {
@@ -54,6 +58,7 @@ func (t *Tags) Get(key string) (*Tag, error) {
return nil, errTagNotExist
}
// Keys returns all keys in Tags
func (t *Tags) Keys() []string {
var keys []string
for _, tag := range t.tags {
@@ -62,6 +67,7 @@ func (t *Tags) Keys() []string {
return keys
}
// Tags returns all tags in Tags
func (t *Tags) Tags() []*Tag {
return t.tags
}

View File

@@ -11,12 +11,13 @@ import (
"github.com/urfave/cli"
)
// TsCommand provides the entry to generting typescript codes
func TsCommand(c *cli.Context) error {
apiFile := c.String("api")
dir := c.String("dir")
webApi := c.String("webapi")
webAPI := c.String("webapi")
caller := c.String("caller")
unwrapApi := c.Bool("unwrap")
unwrapAPI := c.Bool("unwrap")
if len(apiFile) == 0 {
return errors.New("missing -api")
}
@@ -32,7 +33,7 @@ func TsCommand(c *cli.Context) error {
}
logx.Must(util.MkdirIfNotExist(dir))
logx.Must(genHandler(dir, webApi, caller, api, unwrapApi))
logx.Must(genHandler(dir, webAPI, caller, api, unwrapAPI))
logx.Must(genComponents(dir, api))
fmt.Println(aurora.Green("Done."))

View File

@@ -18,7 +18,7 @@ const (
`
)
func genHandler(dir, webApi, caller string, api *spec.ApiSpec, unwrapApi bool) error {
func genHandler(dir, webAPI, caller string, api *spec.ApiSpec, unwrapAPI bool) error {
filename := strings.Replace(api.Service.Name, "-api", "", 1) + ".ts"
if err := util.RemoveIfExist(path.Join(dir, filename)); err != nil {
return err
@@ -37,11 +37,11 @@ func genHandler(dir, webApi, caller string, api *spec.ApiSpec, unwrapApi bool) e
caller = "webapi"
}
importCaller := caller
if unwrapApi {
if unwrapAPI {
importCaller = "{ " + importCaller + " }"
}
if len(webApi) > 0 {
imports += `import ` + importCaller + ` from ` + "\"" + webApi + "\""
if len(webAPI) > 0 {
imports += `import ` + importCaller + ` from ` + "\"" + webAPI + "\""
}
if len(api.Types) != 0 {
@@ -53,7 +53,7 @@ func genHandler(dir, webApi, caller string, api *spec.ApiSpec, unwrapApi bool) e
imports += fmt.Sprintf(`%sexport * from "%s"`, util.NL, "./"+outputFile)
}
apis, err := genApi(api, caller)
apis, err := genAPI(api, caller)
if err != nil {
return err
}
@@ -65,7 +65,7 @@ func genHandler(dir, webApi, caller string, api *spec.ApiSpec, unwrapApi bool) e
})
}
func genApi(api *spec.ApiSpec, caller string) (string, error) {
func genAPI(api *spec.ApiSpec, caller string) (string, error) {
var builder strings.Builder
for _, group := range api.Service.Groups {
for _, route := range group.Routes {
@@ -157,7 +157,7 @@ func callParamsForRoute(route spec.Route, group spec.Group) string {
}
func pathForRoute(route spec.Route, group spec.Group) string {
prefix := group.GetAnnotation("pathPrefix")
prefix := group.GetAnnotation(pathPrefix)
if len(prefix) == 0 {
return "\"" + route.Path + "\""
}

View File

@@ -19,7 +19,7 @@ func writeProperty(writer io.Writer, member spec.Member, indent int) error {
}
optionalTag := ""
if member.IsOptional() || member.IsOmitempty() {
if member.IsOptional() || member.IsOmitEmpty() {
optionalTag = "?"
}
name, err := member.GetPropertyName()

View File

@@ -5,6 +5,7 @@ import (
"unicode"
)
// IsUpperCase returns true if the rune in A-Z
func IsUpperCase(r rune) bool {
if r >= 'A' && r <= 'Z' {
return true
@@ -12,6 +13,7 @@ func IsUpperCase(r rune) bool {
return false
}
// IsLowerCase returns true if the rune in a-z
func IsLowerCase(r rune) bool {
if r >= 'a' && r <= 'z' {
return true
@@ -19,6 +21,7 @@ func IsLowerCase(r rune) bool {
return false
}
// ToSnakeCase returns a copy string by converting camel case into snake case
func ToSnakeCase(s string) string {
var out []rune
for index, r := range s {
@@ -44,6 +47,7 @@ func ToSnakeCase(s string) string {
return string(out)
}
// ToCamelCase returns a copy string by converting snake case into camel case
func ToCamelCase(s string) string {
s = ToLower(s)
out := []rune{}
@@ -66,6 +70,7 @@ func ToCamelCase(s string) string {
return string(out)
}
// ToLowerCase converts rune into lower case
func ToLowerCase(r rune) rune {
dx := 'A' - 'a'
if IsUpperCase(r) {
@@ -73,6 +78,8 @@ func ToLowerCase(r rune) rune {
}
return r
}
// ToUpperCase converts rune into upper case
func ToUpperCase(r rune) rune {
dx := 'A' - 'a'
if IsLowerCase(r) {
@@ -81,6 +88,7 @@ func ToUpperCase(r rune) rune {
return r
}
// ToLower returns a copy string by converting it into lower
func ToLower(s string) string {
var out []rune
for _, r := range s {
@@ -89,6 +97,7 @@ func ToLower(s string) string {
return string(out)
}
// ToUpper returns a copy string by converting it into upper
func ToUpper(s string) string {
var out []rune
for _, r := range s {
@@ -97,13 +106,7 @@ func ToUpper(s string) string {
return string(out)
}
func LowerFirst(s string) string {
if len(s) == 0 {
return s
}
return ToLower(s[:1]) + s[1:]
}
// UpperFirst converts s[0] into upper case
func UpperFirst(s string) string {
if len(s) == 0 {
return s
@@ -111,6 +114,7 @@ func UpperFirst(s string) string {
return ToUpper(s[:1]) + s[1:]
}
// UnExport converts the first letter into lower case
func UnExport(text string) bool {
var flag bool
str := strings.Map(func(r rune) rune {

View File

@@ -13,6 +13,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util"
)
// MaybeCreateFile creates file if not exists
func MaybeCreateFile(dir, subdir, file string) (fp *os.File, created bool, err error) {
logx.Must(util.MkdirIfNotExist(path.Join(dir, subdir)))
fpath := path.Join(dir, subdir, file)
@@ -26,10 +27,12 @@ func MaybeCreateFile(dir, subdir, file string) (fp *os.File, created bool, err e
return
}
// WrapErr wraps an error with message
func WrapErr(err error, message string) error {
return errors.New(message + ", " + err.Error())
}
// Copy calls io.Copy if the source file and destination file exists
func Copy(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)
if err != nil {
@@ -55,6 +58,7 @@ func Copy(src, dst string) (int64, error) {
return nBytes, err
}
// ComponentName returns component name for typescript
func ComponentName(api *spec.ApiSpec) string {
name := api.Service.Name
if strings.HasSuffix(name, "-api") {
@@ -63,12 +67,14 @@ func ComponentName(api *spec.ApiSpec) string {
return name + "Components"
}
// WriteIndent writes tab spaces
func WriteIndent(writer io.Writer, indent int) {
for i := 0; i < indent; i++ {
fmt.Fprint(writer, "\t")
}
}
// RemoveComment filters comment content
func RemoveComment(line string) string {
var commentIdx = strings.Index(line, "//")
if commentIdx >= 0 {

View File

@@ -9,6 +9,7 @@ import (
"github.com/urfave/cli"
)
// GoValidateApi verifies whether the api has a syntax error
func GoValidateApi(c *cli.Context) error {
apiFile := c.String("api")

View File

@@ -2,35 +2,15 @@ package config
import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util"
"gopkg.in/yaml.v2"
)
const (
configFile = "config.yaml"
configFolder = "config"
// DefaultFormat defines a default naming style
DefaultFormat = "gozero"
)
const defaultYaml = `# namingFormat is used to define the naming format of the generated file name.
# just like time formatting, you can specify the formatting style through the
# two format characters go, and zero. for example: snake format you can
# define as go_zero, camel case format you can it is defined as goZero,
# and even split characters can be specified, such as go#zero. in theory,
# any combination can be used, but the prerequisite must meet the naming conventions
# of each operating system file name. if you want to independently control the file
# naming style of the api, rpc, and model layers, you can set it through apiNamingFormat,
# rpcNamingFormat, modelNamingFormat, and independent control is not enabled by default.
# for more information, please see #{apiNamingFormat},#{rpcNamingFormat},#{modelNamingFormat}
# Note: namingFormat is based on snake or camel string
namingFormat: gozero
`
// Config defines the file naming style
type Config struct {
// NamingFormat is used to define the naming format of the generated file name.
// just like time formatting, you can specify the formatting style through the
@@ -43,6 +23,7 @@ type Config struct {
NamingFormat string `yaml:"namingFormat"`
}
// NewConfig creates an instance for Config
func NewConfig(format string) (*Config, error) {
if len(format) == 0 {
format = DefaultFormat
@@ -52,68 +33,6 @@ func NewConfig(format string) (*Config, error) {
return cfg, err
}
func InitOrGetConfig() (*Config, error) {
var (
defaultConfig Config
)
err := yaml.Unmarshal([]byte(defaultYaml), &defaultConfig)
if err != nil {
return nil, err
}
goctlHome, err := util.GetGoctlHome()
if err != nil {
return nil, err
}
configDir := filepath.Join(goctlHome, configFolder)
configFilename := filepath.Join(configDir, configFile)
if util.FileExists(configFilename) {
data, err := ioutil.ReadFile(configFilename)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(data, &defaultConfig)
if err != nil {
return nil, err
}
err = validate(&defaultConfig)
if err != nil {
return nil, err
}
return &defaultConfig, nil
}
err = util.MkdirIfNotExist(configDir)
if err != nil {
return nil, err
}
f, err := os.Create(configFilename)
if err != nil {
return nil, err
}
defer func() {
_ = f.Close()
}()
_, err = f.WriteString(defaultYaml)
if err != nil {
return nil, err
}
err = validate(&defaultConfig)
if err != nil {
return nil, err
}
return &defaultConfig, nil
}
func validate(cfg *Config) error {
if len(strings.TrimSpace(cfg.NamingFormat)) == 0 {
return errors.New("missing namingFormat")

View File

@@ -37,6 +37,7 @@ func main() {
}
`
// GenConfigCommand provides the entry of goctl config
func GenConfigCommand(c *cli.Context) error {
path, err := filepath.Abs(c.String("path"))
if err != nil {

View File

@@ -22,6 +22,7 @@ const (
cstOffset = 60 * 60 * 8 // 8 hours offset for Chinese Standard Time
)
// Docker describes a dockerfile
type Docker struct {
Chinese bool
GoRelPath string
@@ -32,6 +33,7 @@ type Docker struct {
Argument string
}
// DockerCommand provides the entry for goctl docker
func DockerCommand(c *cli.Context) (err error) {
defer func() {
if err == nil {

View File

@@ -41,22 +41,27 @@ CMD ["./{{.ExeFile}}"{{.Argument}}]
`
)
// Clean deletes all templates files
func Clean() error {
return util.Clean(category)
}
// GenTemplates creates docker template files
func GenTemplates(_ *cli.Context) error {
return initTemplate()
}
// Category returns the const string of docker category
func Category() string {
return category
}
// RevertTemplate recovers the deleted template files
func RevertTemplate(name string) error {
return util.CreateTemplate(category, name, dockerTemplate)
}
// Update deletes and creates new template files
func Update() error {
err := Clean()
if err != nil {

View File

@@ -49,7 +49,7 @@ var (
{
Name: "new",
Usage: "fast create api service",
Action: new.NewService,
Action: new.CreateServiceCommand,
},
{
Name: "format",
@@ -337,7 +337,7 @@ var (
Usage: "whether the command execution environment is from idea plugin. [optional]",
},
},
Action: rpc.RpcNew,
Action: rpc.RPCNew,
},
{
Name: "template",
@@ -348,7 +348,7 @@ var (
Usage: "the target path of proto",
},
},
Action: rpc.RpcTemplate,
Action: rpc.RPCTemplate,
},
{
Name: "proto",
@@ -375,7 +375,7 @@ var (
Usage: "whether the command execution environment is from idea plugin. [optional]",
},
},
Action: rpc.Rpc,
Action: rpc.RPC,
},
},
},

View File

@@ -18,6 +18,7 @@ const (
portLimit = 32767
)
// Deployment describes the k8s deployment yaml
type Deployment struct {
Name string
Namespace string

View File

@@ -9,14 +9,17 @@ import (
const dbTag = "db"
// NewEq wraps builder.Eq
func NewEq(in interface{}) builder.Eq {
return builder.Eq(ToMap(in))
}
// NewGt wraps builder.Gt
func NewGt(in interface{}) builder.Gt {
return builder.Gt(ToMap(in))
}
// ToMap converts interface into map
func ToMap(in interface{}) map[string]interface{} {
out := make(map[string]interface{})
v := reflect.ValueOf(in)
@@ -76,6 +79,7 @@ func FieldNames(in interface{}) []string {
return out
}
// RawFieldNames converts golang struct field into slice string
func RawFieldNames(in interface{}) []string {
out := make([]string, 0)
v := reflect.ValueOf(in)

View File

@@ -10,12 +10,12 @@ import (
type mockedUser struct {
// 自增id
Id string `db:"id" json:"id,omitempty"`
ID string `db:"id" json:"id,omitempty"`
// 姓名
UserName string `db:"user_name" json:"userName,omitempty"`
// 1男,2女
Sex int `db:"sex" json:"sex,omitempty"`
Uuid string `db:"uuid" uuid:"uuid,omitempty"`
UUID string `db:"uuid" uuid:"uuid,omitempty"`
Age int `db:"age" json:"age"`
}
@@ -42,7 +42,7 @@ func TestFieldNames(t *testing.T) {
func TestNewEq(t *testing.T) {
u := &mockedUser{
Id: "123456",
ID: "123456",
UserName: "wahaha",
}
out := NewEq(u)
@@ -54,16 +54,16 @@ func TestNewEq(t *testing.T) {
// @see https://github.com/go-xorm/builder
func TestBuilderSql(t *testing.T) {
u := &mockedUser{
Id: "123123",
ID: "123123",
}
fields := RawFieldNames(u)
eq := NewEq(u)
sql, args, err := builder.Select(fields...).From("user").Where(eq).ToSQL()
fmt.Println(sql, args, err)
actualSql := "SELECT `id`,`user_name`,`sex`,`uuid`,`age` FROM user WHERE id=?"
actualSQL := "SELECT `id`,`user_name`,`sex`,`uuid`,`age` FROM user WHERE id=?"
actualArgs := []interface{}{"123123"}
assert.Equal(t, sql, actualSql)
assert.Equal(t, sql, actualSQL)
assert.Equal(t, args, actualArgs)
}
@@ -76,9 +76,9 @@ func TestBuildSqlDefaultValue(t *testing.T) {
sql, args, err := builder.Select(userFieldsWithRawStringQuote...).From("user").Where(eq).ToSQL()
fmt.Println(sql, args, err)
actualSql := "SELECT `id`,`user_name`,`sex`,`uuid`,`age` FROM user WHERE age=? AND user_name=?"
actualSQL := "SELECT `id`,`user_name`,`sex`,`uuid`,`age` FROM user WHERE age=? AND user_name=?"
actualArgs := []interface{}{0, ""}
assert.Equal(t, sql, actualSql)
assert.Equal(t, sql, actualSQL)
assert.Equal(t, args, actualArgs)
})
@@ -86,9 +86,9 @@ func TestBuildSqlDefaultValue(t *testing.T) {
sql, args, err := builder.Select(userFieldsWithoutRawStringQuote...).From("user").Where(eq).ToSQL()
fmt.Println(sql, args, err)
actualSql := "SELECT id,user_name,sex,uuid,age FROM user WHERE age=? AND user_name=?"
actualSQL := "SELECT id,user_name,sex,uuid,age FROM user WHERE age=? AND user_name=?"
actualArgs := []interface{}{0, ""}
assert.Equal(t, sql, actualSql)
assert.Equal(t, sql, actualSQL)
assert.Equal(t, args, actualArgs)
})
}
@@ -102,9 +102,9 @@ func TestBuilderSqlIn(t *testing.T) {
sql, args, err := builder.Select(userFieldsWithRawStringQuote...).From("user").Where(in).And(gtU).ToSQL()
fmt.Println(sql, args, err)
actualSql := "SELECT `id`,`user_name`,`sex`,`uuid`,`age` FROM user WHERE id IN (?,?,?) AND age>?"
actualSQL := "SELECT `id`,`user_name`,`sex`,`uuid`,`age` FROM user WHERE id IN (?,?,?) AND age>?"
actualArgs := []interface{}{"1", "2", "3", 18}
assert.Equal(t, sql, actualSql)
assert.Equal(t, sql, actualSQL)
assert.Equal(t, args, actualArgs)
}
@@ -113,8 +113,8 @@ func TestBuildSqlLike(t *testing.T) {
sql, args, err := builder.Select(userFieldsWithRawStringQuote...).From("user").Where(like).ToSQL()
fmt.Println(sql, args, err)
actualSql := "SELECT `id`,`user_name`,`sex`,`uuid`,`age` FROM user WHERE name LIKE ?"
actualSQL := "SELECT `id`,`user_name`,`sex`,`uuid`,`age` FROM user WHERE name LIKE ?"
actualArgs := []interface{}{"%wang%"}
assert.Equal(t, sql, actualSql)
assert.Equal(t, sql, actualSQL)
assert.Equal(t, args, actualArgs)
}

View File

@@ -22,13 +22,14 @@ const (
flagDir = "dir"
flagCache = "cache"
flagIdea = "idea"
flagUrl = "url"
flagURL = "url"
flagTable = "table"
flagStyle = "style"
)
var errNotMatched = errors.New("sql not matched")
// MysqlDDL generates model code from ddl
func MysqlDDL(ctx *cli.Context) error {
src := ctx.String(flagSrc)
dir := ctx.String(flagDir)
@@ -43,8 +44,9 @@ func MysqlDDL(ctx *cli.Context) error {
return fromDDl(src, dir, cfg, cache, idea)
}
// MyDataSource generates model code from datasource
func MyDataSource(ctx *cli.Context) error {
url := strings.TrimSpace(ctx.String(flagUrl))
url := strings.TrimSpace(ctx.String(flagURL))
dir := strings.TrimSpace(ctx.String(flagDir))
cache := ctx.Bool(flagCache)
idea := ctx.Bool(flagIdea)

View File

@@ -39,6 +39,7 @@ var commonMysqlDataTypeMap = map[string]string{
"json": "string",
}
// ConvertDataType converts mysql column type into golang type
func ConvertDataType(dataBaseType string, isDefaultNull bool) (string, error) {
tp, ok := commonMysqlDataTypeMap[strings.ToLower(dataBaseType)]
if !ok {

View File

@@ -1,5 +0,0 @@
package gen
import "errors"
var ErrCircleQuery = errors.New("circle query with other fields")

View File

@@ -33,6 +33,7 @@ type (
cfg *config.Config
}
// Option defines a function with argument defaultGenerator
Option func(generator *defaultGenerator)
code struct {
@@ -48,6 +49,7 @@ type (
}
)
// NewDefaultGenerator creates an instance for defaultGenerator
func NewDefaultGenerator(dir string, cfg *config.Config, opt ...Option) (*defaultGenerator, error) {
if dir == "" {
dir = pwd
@@ -75,6 +77,7 @@ func NewDefaultGenerator(dir string, cfg *config.Config, opt ...Option) (*defaul
return generator, nil
}
// WithConsoleOption creates a console option
func WithConsoleOption(c console.Console) Option {
return func(generator *defaultGenerator) {
generator.Console = c
@@ -189,6 +192,7 @@ func (g *defaultGenerator) genFromDDL(source string, withCache bool) (map[string
return m, nil
}
// Table defines mysql table
type Table struct {
parser.Table
CacheKey map[string]Key

View File

@@ -94,7 +94,7 @@ func TestWrapWithRawString(t *testing.T) {
func TestFields(t *testing.T) {
type Student struct {
Id int64 `db:"id"`
ID int64 `db:"id"`
Name string `db:"name"`
Age sql.NullInt64 `db:"age"`
Score sql.NullFloat64 `db:"score"`

View File

@@ -8,17 +8,22 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
// tableName:user
// {{prefix}}=cache
// key:id
// Key defines cache key variable for generating code
type Key struct {
VarExpression string // cacheUserIdPrefix = "cache#User#id#"
Left string // cacheUserIdPrefix
Right string // cache#user#id#
Variable string // userIdKey
KeyExpression string // userIdKey: = fmt.Sprintf("cache#user#id#%v", userId)
DataKeyExpression string // userIdKey: = fmt.Sprintf("cache#user#id#%v", data.userId)
RespKeyExpression string // userIdKey: = fmt.Sprintf("cache#user#id#%v", resp.userId)
// VarExpression likes cacheUserIdPrefix = "cache#User#id#"
VarExpression string
// Left likes cacheUserIdPrefix
Left string
// Right likes cache#user#id#
Right string
// Variable likes userIdKey
Variable string
// KeyExpression likes userIdKey: = fmt.Sprintf("cache#user#id#%v", userId)
KeyExpression string
// DataKeyExpression likes userIdKey: = fmt.Sprintf("cache#user#id#%v", data.userId)
DataKeyExpression string
// RespKeyExpression likes userIdKey: = fmt.Sprintf("cache#user#id#%v", resp.userId)
RespKeyExpression string
}
// key-数据库原始字段名,value-缓存key相关数据

View File

@@ -54,18 +54,22 @@ var templates = map[string]string{
errTemplateFile: template.Error,
}
// Category returns model const value
func Category() string {
return category
}
// Clean deletes all template files
func Clean() error {
return util.Clean(category)
}
// GenTemplates creates template files if not exists
func GenTemplates(_ *cli.Context) error {
return util.InitTemplates(category, templates)
}
// RevertTemplate recovers the delete template files
func RevertTemplate(name string) error {
content, ok := templates[name]
if !ok {
@@ -75,6 +79,7 @@ func RevertTemplate(name string) error {
return util.CreateTemplate(category, name, content)
}
// Update provides template clean and init
func Update() error {
err := Clean()
if err != nil {

View File

@@ -3,10 +3,12 @@ package model
import "github.com/tal-tech/go-zero/core/stores/sqlx"
type (
// InformationSchemaModel defines information schema model
InformationSchemaModel struct {
conn sqlx.SqlConn
}
// Column defines column in table
Column struct {
Name string `db:"COLUMN_NAME"`
DataType string `db:"DATA_TYPE"`
@@ -18,10 +20,12 @@ type (
}
)
// NewInformationSchemaModel creates an instance for InformationSchemaModel
func NewInformationSchemaModel(conn sqlx.SqlConn) *InformationSchemaModel {
return &InformationSchemaModel{conn: conn}
}
// GetAllTables selects all tables from TABLE_SCHEMA
func (m *InformationSchemaModel) GetAllTables(database string) ([]string, error) {
query := `select TABLE_NAME from TABLES where TABLE_SCHEMA = ?`
var tables []string
@@ -33,9 +37,10 @@ func (m *InformationSchemaModel) GetAllTables(database string) ([]string, error)
return tables, nil
}
// FindByTableName finds out the target table by name
func (m *InformationSchemaModel) FindByTableName(db, table string) ([]*Column, error) {
querySql := `select COLUMN_NAME,COLUMN_DEFAULT,IS_NULLABLE,DATA_TYPE,COLUMN_KEY,EXTRA,COLUMN_COMMENT from COLUMNS where TABLE_SCHEMA = ? and TABLE_NAME = ?`
querySQL := `select COLUMN_NAME,COLUMN_DEFAULT,IS_NULLABLE,DATA_TYPE,COLUMN_KEY,EXTRA,COLUMN_COMMENT from COLUMNS where TABLE_SCHEMA = ? and TABLE_NAME = ?`
var reply []*Column
err := m.conn.QueryRows(&reply, querySql, db, table)
err := m.conn.QueryRows(&reply, querySQL, db, table)
return reply, err
}

View File

@@ -21,17 +21,20 @@ const (
const timeImport = "time.Time"
type (
// Table describes a mysql table
Table struct {
Name stringx.String
PrimaryKey Primary
Fields []Field
}
// Primary describes a primary key
Primary struct {
Field
AutoIncrement bool
}
// Field describes a table field
Field struct {
Name stringx.String
DataBaseType string
@@ -41,9 +44,11 @@ type (
Comment string
}
// KeyType types alias of int
KeyType int
)
// Parse parses ddl into golang structure
func Parse(ddl string) (*Table, error) {
stmt, err := sqlparser.ParseStrictDDL(ddl)
if err != nil {
@@ -169,6 +174,7 @@ func getIndexKeyType(indexes []*sqlparser.IndexDefinition) (map[string]KeyType,
return keyMap, nil
}
// ContainsTime determines whether the table field contains time.Time
func (t *Table) ContainsTime() bool {
for _, item := range t.Fields {
if item.DataType == timeImport {
@@ -178,6 +184,7 @@ func (t *Table) ContainsTime() bool {
return false
}
// ConvertColumn provides type conversion for mysql clolumn, primary key lookup
func ConvertColumn(db, table string, in []*model.Column) (*Table, error) {
var reply Table
reply.Name = stringx.From(table)

View File

@@ -1,5 +1,6 @@
package template
// Delete defines a delete template
var Delete = `
func (m *default{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error {
{{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne({{.lowerStartCamelPrimaryKey}})
@@ -17,4 +18,5 @@ func (m *default{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimar
}
`
// DeleteMethod defines a delete template for interface method
var DeleteMethod = `Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error`

View File

@@ -1,5 +1,6 @@
package template
// Error defines an error template
var Error = `package {{.pkg}}
import "github.com/tal-tech/go-zero/core/stores/sqlx"

View File

@@ -1,3 +1,4 @@
package template
// Field defines a filed template for types
var Field = `{{.name}} {{.type}} {{.tag}} {{if .hasComment}}// {{.comment}}{{end}}`

View File

@@ -1,6 +1,7 @@
package template
var (
// Imports defines a import template for model in cache case
Imports = `import (
"database/sql"
"fmt"
@@ -14,6 +15,7 @@ var (
"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
)
`
// ImportsNoCache defines a import template for model in normal case
ImportsNoCache = `import (
"database/sql"
"fmt"

View File

@@ -1,5 +1,6 @@
package template
// Insert defines a template for insert code in model
var Insert = `
func (m *default{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) (sql.Result,error) {
{{if .withCache}}{{if .containsIndexCache}}{{.keys}}
@@ -14,4 +15,5 @@ func (m *default{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelO
}
`
// InsertMethod defines a interface method template for insert code in model
var InsertMethod = `Insert(data {{.upperStartCamelObject}}) (sql.Result,error)`

View File

@@ -1,5 +1,6 @@
package template
// Model defines a template for model
var Model = `package {{.pkg}}
{{.imports}}
{{.vars}}

View File

@@ -1,5 +1,6 @@
package template
// New defines an template for creating model instance
var New = `
func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) {{.upperStartCamelObject}}Model {
return &default{{.upperStartCamelObject}}Model{

View File

@@ -1,3 +1,4 @@
package template
// Tag defines a tag template text
var Tag = "`db:\"{{.field}}\"`"

View File

@@ -1,5 +1,6 @@
package template
// Types defines a template for types in model
var Types = `
type (
{{.upperStartCamelObject}}Model interface{

View File

@@ -1,5 +1,6 @@
package template
// Update defines a template for generating update codes
var Update = `
func (m *default{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) error {
{{if .withCache}}{{.primaryCacheKey}}
@@ -12,4 +13,5 @@ func (m *default{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelO
}
`
// UpdateMethod defines an interface method template for generating update codes
var UpdateMethod = `Update(data {{.upperStartCamelObject}}) error`

View File

@@ -2,6 +2,7 @@ package template
import "fmt"
// Vars defines a template for var block in model
var Vars = fmt.Sprintf(`
var (
{{.lowerStartCamelObject}}FieldNames = builderx.RawFieldNames(&{{.upperStartCamelObject}}{})

View File

@@ -20,11 +20,11 @@ func TestStudentModel(t *testing.T) {
testTable = "`student`"
testUpdateName = "gozero1"
testRowsAffected int64 = 1
testInsertId int64 = 1
testInsertID int64 = 1
)
var data Student
data.Id = testInsertId
data.ID = testInsertID
data.Name = "gozero"
data.Age = sql.NullInt64{
Int64: 1,
@@ -43,14 +43,14 @@ func TestStudentModel(t *testing.T) {
err := mockStudent(func(mock sqlmock.Sqlmock) {
mock.ExpectExec(fmt.Sprintf("insert into %s", testTable)).
WithArgs(data.Name, data.Age, data.Score).
WillReturnResult(sqlmock.NewResult(testInsertId, testRowsAffected))
WillReturnResult(sqlmock.NewResult(testInsertID, testRowsAffected))
}, func(m StudentModel) {
r, err := m.Insert(data)
assert.Nil(t, err)
lastInsertId, err := r.LastInsertId()
lastInsertID, err := r.LastInsertId()
assert.Nil(t, err)
assert.Equal(t, testInsertId, lastInsertId)
assert.Equal(t, testInsertID, lastInsertID)
rowsAffected, err := r.RowsAffected()
assert.Nil(t, err)
@@ -60,17 +60,17 @@ func TestStudentModel(t *testing.T) {
err = mockStudent(func(mock sqlmock.Sqlmock) {
mock.ExpectQuery(fmt.Sprintf("select (.+) from %s", testTable)).
WithArgs(testInsertId).
WillReturnRows(sqlmock.NewRows([]string{"id", "name", "age", "score", "create_time", "update_time"}).AddRow(testInsertId, data.Name, data.Age, data.Score, testTimeValue, testTimeValue))
WithArgs(testInsertID).
WillReturnRows(sqlmock.NewRows([]string{"id", "name", "age", "score", "create_time", "update_time"}).AddRow(testInsertID, data.Name, data.Age, data.Score, testTimeValue, testTimeValue))
}, func(m StudentModel) {
result, err := m.FindOne(testInsertId)
result, err := m.FindOne(testInsertID)
assert.Nil(t, err)
assert.Equal(t, *result, data)
})
assert.Nil(t, err)
err = mockStudent(func(mock sqlmock.Sqlmock) {
mock.ExpectExec(fmt.Sprintf("update %s", testTable)).WithArgs(testUpdateName, data.Age, data.Score, testInsertId).WillReturnResult(sqlmock.NewResult(testInsertId, testRowsAffected))
mock.ExpectExec(fmt.Sprintf("update %s", testTable)).WithArgs(testUpdateName, data.Age, data.Score, testInsertID).WillReturnResult(sqlmock.NewResult(testInsertID, testRowsAffected))
}, func(m StudentModel) {
data.Name = testUpdateName
err := m.Update(data)
@@ -80,19 +80,19 @@ func TestStudentModel(t *testing.T) {
err = mockStudent(func(mock sqlmock.Sqlmock) {
mock.ExpectQuery(fmt.Sprintf("select (.+) from %s ", testTable)).
WithArgs(testInsertId).
WillReturnRows(sqlmock.NewRows([]string{"id", "name", "age", "score", "create_time", "update_time"}).AddRow(testInsertId, data.Name, data.Age, data.Score, testTimeValue, testTimeValue))
WithArgs(testInsertID).
WillReturnRows(sqlmock.NewRows([]string{"id", "name", "age", "score", "create_time", "update_time"}).AddRow(testInsertID, data.Name, data.Age, data.Score, testTimeValue, testTimeValue))
}, func(m StudentModel) {
result, err := m.FindOne(testInsertId)
result, err := m.FindOne(testInsertID)
assert.Nil(t, err)
assert.Equal(t, *result, data)
})
assert.Nil(t, err)
err = mockStudent(func(mock sqlmock.Sqlmock) {
mock.ExpectExec(fmt.Sprintf("delete from %s where `id` = ?", testTable)).WithArgs(testInsertId).WillReturnResult(sqlmock.NewResult(testInsertId, testRowsAffected))
mock.ExpectExec(fmt.Sprintf("delete from %s where `id` = ?", testTable)).WithArgs(testInsertID).WillReturnResult(sqlmock.NewResult(testInsertID, testRowsAffected))
}, func(m StudentModel) {
err := m.Delete(testInsertId)
err := m.Delete(testInsertID)
assert.Nil(t, err)
})
assert.Nil(t, err)
@@ -109,11 +109,11 @@ func TestUserModel(t *testing.T) {
testGender = "男"
testNickname = "test_nickname"
testRowsAffected int64 = 1
testInsertId int64 = 1
testInsertID int64 = 1
)
var data User
data.Id = testInsertId
data.ID = testInsertID
data.User = testUser
data.Name = "gozero"
data.Password = testPassword
@@ -126,14 +126,14 @@ func TestUserModel(t *testing.T) {
err := mockUser(func(mock sqlmock.Sqlmock) {
mock.ExpectExec(fmt.Sprintf("insert into %s", testTable)).
WithArgs(data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname).
WillReturnResult(sqlmock.NewResult(testInsertId, testRowsAffected))
WillReturnResult(sqlmock.NewResult(testInsertID, testRowsAffected))
}, func(m UserModel) {
r, err := m.Insert(data)
assert.Nil(t, err)
lastInsertId, err := r.LastInsertId()
lastInsertID, err := r.LastInsertId()
assert.Nil(t, err)
assert.Equal(t, testInsertId, lastInsertId)
assert.Equal(t, testInsertID, lastInsertID)
rowsAffected, err := r.RowsAffected()
assert.Nil(t, err)
@@ -143,17 +143,17 @@ func TestUserModel(t *testing.T) {
err = mockUser(func(mock sqlmock.Sqlmock) {
mock.ExpectQuery(fmt.Sprintf("select (.+) from %s", testTable)).
WithArgs(testInsertId).
WillReturnRows(sqlmock.NewRows([]string{"id", "user", "name", "password", "mobile", "gender", "nickname", "create_time", "update_time"}).AddRow(testInsertId, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, testTimeValue, testTimeValue))
WithArgs(testInsertID).
WillReturnRows(sqlmock.NewRows([]string{"id", "user", "name", "password", "mobile", "gender", "nickname", "create_time", "update_time"}).AddRow(testInsertID, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, testTimeValue, testTimeValue))
}, func(m UserModel) {
result, err := m.FindOne(testInsertId)
result, err := m.FindOne(testInsertID)
assert.Nil(t, err)
assert.Equal(t, *result, data)
})
assert.Nil(t, err)
err = mockUser(func(mock sqlmock.Sqlmock) {
mock.ExpectExec(fmt.Sprintf("update %s", testTable)).WithArgs(data.User, testUpdateName, data.Password, data.Mobile, data.Gender, data.Nickname, testInsertId).WillReturnResult(sqlmock.NewResult(testInsertId, testRowsAffected))
mock.ExpectExec(fmt.Sprintf("update %s", testTable)).WithArgs(data.User, testUpdateName, data.Password, data.Mobile, data.Gender, data.Nickname, testInsertID).WillReturnResult(sqlmock.NewResult(testInsertID, testRowsAffected))
}, func(m UserModel) {
data.Name = testUpdateName
err := m.Update(data)
@@ -163,19 +163,19 @@ func TestUserModel(t *testing.T) {
err = mockUser(func(mock sqlmock.Sqlmock) {
mock.ExpectQuery(fmt.Sprintf("select (.+) from %s ", testTable)).
WithArgs(testInsertId).
WillReturnRows(sqlmock.NewRows([]string{"id", "user", "name", "password", "mobile", "gender", "nickname", "create_time", "update_time"}).AddRow(testInsertId, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, testTimeValue, testTimeValue))
WithArgs(testInsertID).
WillReturnRows(sqlmock.NewRows([]string{"id", "user", "name", "password", "mobile", "gender", "nickname", "create_time", "update_time"}).AddRow(testInsertID, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, testTimeValue, testTimeValue))
}, func(m UserModel) {
result, err := m.FindOne(testInsertId)
result, err := m.FindOne(testInsertID)
assert.Nil(t, err)
assert.Equal(t, *result, data)
})
assert.Nil(t, err)
err = mockUser(func(mock sqlmock.Sqlmock) {
mock.ExpectExec(fmt.Sprintf("delete from %s where `id` = ?", testTable)).WithArgs(testInsertId).WillReturnResult(sqlmock.NewResult(testInsertId, testRowsAffected))
mock.ExpectExec(fmt.Sprintf("delete from %s where `id` = ?", testTable)).WithArgs(testInsertID).WillReturnResult(sqlmock.NewResult(testInsertID, testRowsAffected))
}, func(m UserModel) {
err := m.Delete(testInsertId)
err := m.Delete(testInsertID)
assert.Nil(t, err)
})
assert.Nil(t, err)

View File

@@ -19,10 +19,11 @@ var (
studentRowsExpectAutoSet = strings.Join(stringx.Remove(studentFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
studentRowsWithPlaceHolder = strings.Join(stringx.Remove(studentFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
cacheStudentIdPrefix = "cache#Student#id#"
cacheStudentIDPrefix = "cache#Student#id#"
)
type (
// StudentModel defines a model for Student
StudentModel interface {
Insert(data Student) (sql.Result, error)
FindOne(id int64) (*Student, error)
@@ -35,8 +36,9 @@ type (
table string
}
// Student defines an data structure for mysql
Student struct {
Id int64 `db:"id"`
ID int64 `db:"id"`
Name string `db:"name"`
Age sql.NullInt64 `db:"age"`
Score sql.NullFloat64 `db:"score"`
@@ -45,6 +47,7 @@ type (
}
)
// NewStudentModel creates an instance for StudentModel
func NewStudentModel(conn sqlx.SqlConn, c cache.CacheConf) StudentModel {
return &defaultStudentModel{
CachedConn: sqlc.NewConn(conn, c),
@@ -60,9 +63,9 @@ func (m *defaultStudentModel) Insert(data Student) (sql.Result, error) {
}
func (m *defaultStudentModel) FindOne(id int64) (*Student, error) {
studentIdKey := fmt.Sprintf("%s%v", cacheStudentIdPrefix, id)
studentIDKey := fmt.Sprintf("%s%v", cacheStudentIDPrefix, id)
var resp Student
err := m.QueryRow(&resp, studentIdKey, func(conn sqlx.SqlConn, v interface{}) error {
err := m.QueryRow(&resp, studentIDKey, func(conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", studentRows, m.table)
return conn.QueryRow(v, query, id)
})
@@ -77,26 +80,26 @@ func (m *defaultStudentModel) FindOne(id int64) (*Student, error) {
}
func (m *defaultStudentModel) Update(data Student) error {
studentIdKey := fmt.Sprintf("%s%v", cacheStudentIdPrefix, data.Id)
studentIDKey := fmt.Sprintf("%s%v", cacheStudentIDPrefix, data.ID)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, studentRowsWithPlaceHolder)
return conn.Exec(query, data.Name, data.Age, data.Score, data.Id)
}, studentIdKey)
return conn.Exec(query, data.Name, data.Age, data.Score, data.ID)
}, studentIDKey)
return err
}
func (m *defaultStudentModel) Delete(id int64) error {
studentIdKey := fmt.Sprintf("%s%v", cacheStudentIdPrefix, id)
studentIDKey := fmt.Sprintf("%s%v", cacheStudentIDPrefix, id)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
return conn.Exec(query, id)
}, studentIdKey)
}, studentIDKey)
return err
}
func (m *defaultStudentModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheStudentIdPrefix, primary)
return fmt.Sprintf("%s%v", cacheStudentIDPrefix, primary)
}
func (m *defaultStudentModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {

View File

@@ -20,6 +20,7 @@ var (
)
type (
// UserModel defines a model for user
UserModel interface {
Insert(data User) (sql.Result, error)
FindOne(id int64) (*User, error)
@@ -35,8 +36,9 @@ type (
table string
}
// User defines an data structure for mysql
User struct {
Id int64 `db:"id"`
ID int64 `db:"id"`
User string `db:"user"` // 用户
Name string `db:"name"` // 用户名称
Password string `db:"password"` // 用户密码
@@ -48,6 +50,7 @@ type (
}
)
// NewUserModel creates an instance for UserModel
func NewUserModel(conn sqlx.SqlConn) UserModel {
return &defaultUserModel{
conn: conn,
@@ -119,7 +122,7 @@ func (m *defaultUserModel) FindOneByMobile(mobile string) (*User, error) {
func (m *defaultUserModel) Update(data User) error {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, userRowsWithPlaceHolder)
_, err := m.conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id)
_, err := m.conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.ID)
return err
}

View File

@@ -2,4 +2,5 @@ package model
import "github.com/tal-tech/go-zero/core/stores/sqlx"
// ErrNotFound types an alias for sqlx.ErrNotFound
var ErrNotFound = sqlx.ErrNotFound

View File

@@ -13,9 +13,13 @@ import (
const tagName = "db"
var (
ErrNotMatchDestination = errors.New("not matching destination to scan")
ErrNotReadableValue = errors.New("value not addressable or interfaceable")
ErrNotSettable = errors.New("passed in variable is not settable")
// ErrNotMatchDestination defines an error for mismatching case
ErrNotMatchDestination = errors.New("not matching destination to scan")
// ErrNotReadableValue defines an error for the value is not addressable or interfaceable
ErrNotReadableValue = errors.New("value not addressable or interfaceable")
// ErrNotSettable defines an error for the variable is not settable
ErrNotSettable = errors.New("passed in variable is not settable")
// ErrUnsupportedValueType deinfes an error for unsupported unmarshal type
ErrUnsupportedValueType = errors.New("unsupported unmarshal type")
)

View File

@@ -9,6 +9,7 @@ import (
)
type (
// MockConn defines a mock connection instance for mysql
MockConn struct {
db *sql.DB
}
@@ -17,43 +18,51 @@ type (
}
)
// NewMockConn creates an instance for MockConn
func NewMockConn(db *sql.DB) *MockConn {
return &MockConn{db: db}
}
// Exec executes sql and returns the result
func (conn *MockConn) Exec(query string, args ...interface{}) (sql.Result, error) {
return exec(conn.db, query, args...)
}
// Prepare executes sql by sql.DB
func (conn *MockConn) Prepare(query string) (sqlx.StmtSession, error) {
st, err := conn.db.Prepare(query)
return statement{stmt: st}, err
}
// QueryRow executes sql and returns a query row
func (conn *MockConn) QueryRow(v interface{}, q string, args ...interface{}) error {
return query(conn.db, func(rows *sql.Rows) error {
return unmarshalRow(v, rows, true)
}, q, args...)
}
// QueryRowPartial executes sql and returns a partial query row
func (conn *MockConn) QueryRowPartial(v interface{}, q string, args ...interface{}) error {
return query(conn.db, func(rows *sql.Rows) error {
return unmarshalRow(v, rows, false)
}, q, args...)
}
// QueryRows executes sql and returns query rows
func (conn *MockConn) QueryRows(v interface{}, q string, args ...interface{}) error {
return query(conn.db, func(rows *sql.Rows) error {
return unmarshalRows(v, rows, true)
}, q, args...)
}
// QueryRowsPartial executes sql and returns partial query rows
func (conn *MockConn) QueryRowsPartial(v interface{}, q string, args ...interface{}) error {
return query(conn.db, func(rows *sql.Rows) error {
return unmarshalRows(v, rows, false)
}, q, args...)
}
// Transact is the implemention of sqlx.SqlConn, nothing to do
func (conn *MockConn) Transact(func(session sqlx.Session) error) error {
return nil
}

View File

@@ -94,11 +94,6 @@ func format(query string, args ...interface{}) (string, error) {
return b.String(), nil
}
func logInstanceError(datasource string, err error) {
datasource = desensitize(datasource)
logx.Errorf("Error on getting sql instance of %s: %v", datasource, err)
}
func logSqlError(stmt string, err error) {
if err != nil && err != ErrNotFound {
logx.Errorf("stmt: %s, error: %s", stmt, err.Error())

View File

@@ -5,7 +5,7 @@ import (
"path/filepath"
)
// expression: globbing patterns
// MatchFiles returns the match values by globbing patterns
func MatchFiles(in string) ([]string, error) {
dir, pattern := filepath.Split(in)
abs, err := filepath.Abs(dir)

View File

@@ -1,5 +1,6 @@
package util
// TrimStringSlice returns a copy slice without empty string item
func TrimStringSlice(list []string) []string {
var out []string
for _, item := range list {

View File

@@ -22,6 +22,7 @@ import (
const pluginArg = "_plugin"
// Plugin defines an api plugin
type Plugin struct {
Api *spec.ApiSpec
ApiFilePath string
@@ -29,6 +30,7 @@ type Plugin struct {
Dir string
}
// PluginCommand is the entry of goctl api plugin
func PluginCommand(c *cli.Context) error {
ex, err := os.Executable()
if err != nil {
@@ -155,6 +157,7 @@ func downloadFile(filepath string, url string) error {
return err
}
// NewPlugin returns contextual resources when written in other languages
func NewPlugin() (*Plugin, error) {
var plugin Plugin
content, err := ioutil.ReadAll(os.Stdin)

View File

@@ -9,10 +9,10 @@ import (
"github.com/urfave/cli"
)
// Rpc is to generate rpc service code from a proto file by specifying a proto file using flag src,
// RPC is to generate rpc service code from a proto file by specifying a proto file using flag src,
// you can specify a target folder for code generation, when the proto file has import, you can specify
// the import search directory through the proto_path command, for specific usage, please refer to protoc -h
func Rpc(c *cli.Context) error {
func RPC(c *cli.Context) error {
src := c.String("src")
out := c.String("dir")
style := c.String("style")
@@ -24,7 +24,7 @@ func Rpc(c *cli.Context) error {
return errors.New("missing -dir")
}
g, err := generator.NewDefaultRpcGenerator(style)
g, err := generator.NewDefaultRPCGenerator(style)
if err != nil {
return err
}
@@ -32,9 +32,9 @@ func Rpc(c *cli.Context) error {
return g.Generate(src, out, protoImportPath)
}
// RpcNew is to generate rpc greet service, this greet service can speed
// RPCNew is to generate rpc greet service, this greet service can speed
// up your understanding of the zrpc service structure
func RpcNew(c *cli.Context) error {
func RPCNew(c *cli.Context) error {
rpcname := c.Args().First()
ext := filepath.Ext(rpcname)
if len(ext) > 0 {
@@ -54,7 +54,7 @@ func RpcNew(c *cli.Context) error {
return err
}
g, err := generator.NewDefaultRpcGenerator(style)
g, err := generator.NewDefaultRPCGenerator(style)
if err != nil {
return err
}
@@ -62,7 +62,8 @@ func RpcNew(c *cli.Context) error {
return g.Generate(src, filepath.Dir(src), nil)
}
func RpcTemplate(c *cli.Context) error {
// RPCTemplate is the entry for generate rpc template
func RPCTemplate(c *cli.Context) error {
protoFile := c.String("o")
if len(protoFile) == 0 {
return errors.New("missing -o")

View File

@@ -12,6 +12,9 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/vars"
)
// Run provides the execution of shell scripts in golang,
// which can support macOS, Windows, and Linux operating systems.
// Other operating systems are currently not supported
func Run(arg string, dir string, in ...*bytes.Buffer) (string, error) {
goos := runtime.GOOS
var cmd *exec.Cmd

View File

@@ -6,18 +6,22 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/console"
)
type defaultGenerator struct {
// DefaultGenerator defines the environment needs of rpc service generation
type DefaultGenerator struct {
log console.Console
}
func NewDefaultGenerator() *defaultGenerator {
// NewDefaultGenerator returns an instance of DefaultGenerator
func NewDefaultGenerator() *DefaultGenerator {
log := console.NewColorConsole()
return &defaultGenerator{
return &DefaultGenerator{
log: log,
}
}
func (g *defaultGenerator) Prepare() error {
// Prepare provides environment detection generated by rpc service,
// including go environment, protoc, whether protoc-gen-go is installed or not
func (g *DefaultGenerator) Prepare() error {
_, err := exec.LookPath("go")
if err != nil {
return err

View File

@@ -10,27 +10,33 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
type RpcGenerator struct {
// RPCGenerator defines a generator and configure
type RPCGenerator struct {
g Generator
cfg *conf.Config
}
func NewDefaultRpcGenerator(style string) (*RpcGenerator, error) {
// NewDefaultRPCGenerator wraps Generator with configure
func NewDefaultRPCGenerator(style string) (*RPCGenerator, error) {
cfg, err := conf.NewConfig(style)
if err != nil {
return nil, err
}
return NewRpcGenerator(NewDefaultGenerator(), cfg), nil
return NewRPCGenerator(NewDefaultGenerator(), cfg), nil
}
func NewRpcGenerator(g Generator, cfg *conf.Config) *RpcGenerator {
return &RpcGenerator{
// NewRPCGenerator creates an instance for RPCGenerator
func NewRPCGenerator(g Generator, cfg *conf.Config) *RPCGenerator {
return &RPCGenerator{
g: g,
cfg: cfg,
}
}
func (g *RpcGenerator) Generate(src, target string, protoImportPath []string) error {
// Generate generates an rpc service, through the proto file,
// code storage directory, and proto import parameters to control
// the source file and target location of the rpc service that needs to be generated
func (g *RPCGenerator) Generate(src, target string, protoImportPath []string) error {
abs, err := filepath.Abs(target)
if err != nil {
return err

View File

@@ -27,7 +27,7 @@ func TestRpcGenerate(t *testing.T) {
return
}
projectName := stringx.Rand()
g := NewRpcGenerator(dispatcher, cfg)
g := NewRPCGenerator(dispatcher, cfg)
// case go path
src := filepath.Join(build.Default.GOPATH, "src")

View File

@@ -61,7 +61,9 @@ func (m *default{{.serviceName}}) {{.method}}(ctx context.Context,in *{{.pbReque
`
)
func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
// GenCall generates the rpc client code, which is the entry point for the rpc service call.
// It is a layer of encapsulation for the rpc client and shields the details in the pb.
func (g *DefaultGenerator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetCall()
service := proto.Service
head := util.GetHead(proto.Name)
@@ -105,7 +107,7 @@ func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf
return err
}
func (g *defaultGenerator) genFunction(goPackage string, service parser.Service) ([]string, error) {
func (g *DefaultGenerator) genFunction(goPackage string, service parser.Service) ([]string, error) {
functions := make([]string, 0)
for _, rpc := range service.RPC {
text, err := util.LoadTemplate(category, callFunctionTemplateFile, callFunctionTemplate)
@@ -133,7 +135,7 @@ func (g *defaultGenerator) genFunction(goPackage string, service parser.Service)
return functions, nil
}
func (g *defaultGenerator) getInterfaceFuncs(service parser.Service) ([]string, error) {
func (g *DefaultGenerator) getInterfaceFuncs(service parser.Service) ([]string, error) {
functions := make([]string, 0)
for _, rpc := range service.RPC {

View File

@@ -20,7 +20,11 @@ type Config struct {
}
`
func (g *defaultGenerator) GenConfig(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
// GenConfig generates the configuration structure definition file of the rpc service,
// which contains the zrpc.RpcServerConf configuration item by default.
// You can specify the naming style of the target file name through config.Config. For details,
// see https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/config.go
func (g *DefaultGenerator) GenConfig(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
dir := ctx.GetConfig()
configFilename, err := format.FileNamingFormat(cfg.NamingFormat, "config")
if err != nil {

View File

@@ -5,6 +5,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
)
// Generator defines a generator interface to describe how to generate rpc service
type Generator interface {
Prepare() error
GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error

View File

@@ -20,7 +20,9 @@ Etcd:
Key: {{.serviceName}}.rpc
`
func (g *defaultGenerator) GenEtc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
// GenEtc generates the yaml configuration file of the rpc service,
// including host, port monitoring configuration items and etcd configuration
func (g *DefaultGenerator) GenEtc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
dir := ctx.GetEtc()
etcFilename, err := format.FileNamingFormat(cfg.NamingFormat, ctx.GetServiceName().Source())
if err != nil {

View File

@@ -48,7 +48,8 @@ func (l *{{.logicName}}) {{.method}} (in {{.request}}) ({{.response}}, error) {
`
)
func (g *defaultGenerator) GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
// GenLogic generates the logic file of the rpc service, which corresponds to the RPC definition items in proto.
func (g *DefaultGenerator) GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetLogic()
for _, rpc := range proto.Service.RPC {
logicFilename, err := format.FileNamingFormat(cfg.NamingFormat, rpc.Name+"_logic")
@@ -81,7 +82,7 @@ func (g *defaultGenerator) GenLogic(ctx DirContext, proto parser.Proto, cfg *con
return nil
}
func (g *defaultGenerator) genLogicFunction(goPackage string, rpc *parser.RPC) (string, error) {
func (g *DefaultGenerator) genLogicFunction(goPackage string, rpc *parser.RPC) (string, error) {
var functions = make([]string, 0)
text, err := util.LoadTemplate(category, logicFuncTemplateFileFile, logicFunctionTemplate)
if err != nil {

View File

@@ -12,9 +12,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
const mainTemplate = `{{.head}}
package main
const mainTemplate = `package main
import (
"flag"
@@ -47,7 +45,8 @@ func main() {
}
`
func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
// GenMain generates the main file of the rpc service, which is an rpc service program call entry
func (g *DefaultGenerator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
mainFilename, err := format.FileNamingFormat(cfg.NamingFormat, ctx.GetServiceName().Source())
if err != nil {
return err
@@ -60,14 +59,12 @@ func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf
remoteImport := fmt.Sprintf(`"%v"`, ctx.GetServer().Package)
configImport := fmt.Sprintf(`"%v"`, ctx.GetConfig().Package)
imports = append(imports, configImport, pbImport, remoteImport, svcImport)
head := util.GetHead(proto.Name)
text, err := util.LoadTemplate(category, mainTemplateFile, mainTemplate)
if err != nil {
return err
}
return util.With("main").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"head": head,
"serviceName": strings.ToLower(ctx.GetServiceName().ToCamel()),
"imports": strings.Join(imports, util.NL),
"pkg": proto.PbPackage,

View File

@@ -10,7 +10,9 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
)
func (g *defaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, _ *conf.Config) error {
// GenPb generates the pb.go file, which is a layer of packaging for protoc to generate gprc,
// but the commands and flags in protoc are not completely joined in goctl. At present, proto_path(-I) is introduced
func (g *DefaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, _ *conf.Config) error {
dir := ctx.GetPb()
cw := new(bytes.Buffer)
base := filepath.Dir(proto.Src)

View File

@@ -45,7 +45,8 @@ func (s *{{.server}}Server) {{.method}} (ctx context.Context, in {{.request}}) (
`
)
func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
// GenServer generates rpc server file, which is an implementation of rpc server
func (g *DefaultGenerator) GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetServer()
logicImport := fmt.Sprintf(`"%v"`, ctx.GetLogic().Package)
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
@@ -81,7 +82,7 @@ func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto, cfg *co
return err
}
func (g *defaultGenerator) genFunctions(goPackage string, service parser.Service) ([]string, error) {
func (g *DefaultGenerator) genFunctions(goPackage string, service parser.Service) ([]string, error) {
var functionList []string
for _, rpc := range service.RPC {
text, err := util.LoadTemplate(category, serverFuncTemplateFile, functionTemplate)

View File

@@ -25,7 +25,9 @@ func NewServiceContext(c config.Config) *ServiceContext {
}
`
func (g *defaultGenerator) GenSvc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
// GenSvc generates the servicecontext.go file, which is the resource dependency of a service,
// such as rpc dependency, model dependency, etc.
func (g *DefaultGenerator) GenSvc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
dir := ctx.GetSvc()
svcFilename, err := format.FileNamingFormat(cfg.NamingFormat, "service_context")
if err != nil {

View File

@@ -23,6 +23,7 @@ const (
)
type (
// DirContext defines a rpc service directories context
DirContext interface {
GetCall() Dir
GetEtc() Dir
@@ -36,11 +37,13 @@ type (
GetServiceName() stringx.String
}
// Dir defines a directory
Dir struct {
Base string
Filename string
Package string
}
defaultDirContext struct {
inner map[string]Dir
serviceName stringx.String
@@ -159,6 +162,7 @@ func (d *defaultDirContext) GetServiceName() stringx.String {
return d.serviceName
}
// Valid returns true if the directory is valid
func (d *Dir) Valid() bool {
return len(d.Filename) > 0 && len(d.Package) > 0
}

View File

@@ -25,6 +25,7 @@ service {{.serviceName}} {
}
`
// ProtoTmpl returns an sample of a proto file
func ProtoTmpl(out string) error {
protoFilename := filepath.Base(out)
serviceName := stringx.From(strings.TrimSuffix(protoFilename, filepath.Ext(protoFilename)))

View File

@@ -38,10 +38,13 @@ var templates = map[string]string{
rpcTemplateFile: rpcTemplateText,
}
// GenTemplates is the entry for command goctl template,
// it will create the specified category
func GenTemplates(_ *cli.Context) error {
return util.InitTemplates(category, templates)
}
// RevertTemplate restores the deleted template files
func RevertTemplate(name string) error {
content, ok := templates[name]
if !ok {
@@ -50,10 +53,13 @@ func RevertTemplate(name string) error {
return util.CreateTemplate(category, name, content)
}
// Clean deletes all template files
func Clean() error {
return util.Clean(category)
}
// Update is used to update the template files, it will delete the existing old templates at first,
// and then create the latest template files
func Update() error {
err := Clean()
if err != nil {
@@ -63,6 +69,7 @@ func Update() error {
return util.InitTemplates(category, templates)
}
// Category returns a const string value for rpc template category
func Category() string {
return category
}

View File

@@ -2,6 +2,7 @@ package parser
import "github.com/emicklei/proto"
// GetComment returns content with prefix //
func GetComment(comment *proto.Comment) string {
if comment == nil {
return ""

View File

@@ -2,6 +2,7 @@ package parser
import "github.com/emicklei/proto"
// Import embeds proto.Import
type Import struct {
*proto.Import
}

View File

@@ -1,7 +1,8 @@
package parser
import pr "github.com/emicklei/proto"
import "github.com/emicklei/proto"
// Message embeds proto.Message
type Message struct {
*pr.Message
*proto.Message
}

View File

@@ -2,6 +2,7 @@ package parser
import "github.com/emicklei/proto"
// Option embeds proto.Option
type Option struct {
*proto.Option
}

View File

@@ -14,14 +14,18 @@ import (
)
type (
defaultProtoParser struct{}
// DefaultProtoParser types a empty struct
DefaultProtoParser struct{}
)
func NewDefaultProtoParser() *defaultProtoParser {
return &defaultProtoParser{}
// NewDefaultProtoParser creates a new instance
func NewDefaultProtoParser() *DefaultProtoParser {
return &DefaultProtoParser{}
}
func (p *defaultProtoParser) Parse(src string) (Proto, error) {
// Parse provides to parse the proto file into a golang structure,
// which is convenient for subsequent rpc generation and use
func (p *DefaultProtoParser) Parse(src string) (Proto, error) {
var ret Proto
abs, err := filepath.Abs(src)
@@ -101,7 +105,7 @@ func (p *defaultProtoParser) Parse(src string) (Proto, error) {
return ret, nil
}
// see google.golang.org/protobuf@v1.25.0/internal/strs/strings.go:71
// GoSanitized copy from protobuf, for more information, please see google.golang.org/protobuf@v1.25.0/internal/strs/strings.go:71
func GoSanitized(s string) string {
// Sanitize the input to the set of valid characters,
// which must be '_' or be in the Unicode L or N categories.
@@ -121,7 +125,7 @@ func GoSanitized(s string) string {
return s
}
// copy from github.com/golang/protobuf@v1.4.2/protoc-gen-go/generator/generator.go:2648
// CamelCase copy from protobuf, for more information, please see github.com/golang/protobuf@v1.4.2/protoc-gen-go/generator/generator.go:2648
func CamelCase(s string) string {
if s == "" {
return ""

View File

@@ -1,5 +1,6 @@
package parser
// Proto describes a proto file,
type Proto struct {
Src string
Name string

View File

@@ -2,6 +2,7 @@ package parser
import "github.com/emicklei/proto"
// RPC embeds proto.RPC
type RPC struct {
*proto.RPC
}

View File

@@ -2,6 +2,8 @@ package parser
import "github.com/emicklei/proto"
// Service describes the rpc service, which is the relevant
// content after the translation of the proto file
type Service struct {
*proto.Service
RPC []*RPC

View File

@@ -16,6 +16,7 @@ import (
const templateParentPath = "/"
// GenTemplates wtites the latest template text into file which is not exists
func GenTemplates(ctx *cli.Context) error {
if err := errorx.Chain(
func() error {
@@ -48,6 +49,7 @@ func GenTemplates(ctx *cli.Context) error {
return nil
}
// CleanTemplates deletes all templates
func CleanTemplates(_ *cli.Context) error {
err := errorx.Chain(
func() error {
@@ -68,6 +70,8 @@ func CleanTemplates(_ *cli.Context) error {
return nil
}
// UpdateTemplates wtites the latest template text into file,
// it will delete the oldler templates if there are exists
func UpdateTemplates(ctx *cli.Context) (err error) {
category := ctx.String("category")
defer func() {
@@ -92,6 +96,7 @@ func UpdateTemplates(ctx *cli.Context) (err error) {
}
}
// RevertTemplates will overwrite the old template content with the new template
func RevertTemplates(ctx *cli.Context) (err error) {
category := ctx.String("category")
filename := ctx.String("name")

View File

@@ -2,6 +2,7 @@ package config
import "github.com/tal-tech/go-zero/core/logx"
// Config defines a service configure for goctl update
type Config struct {
logx.LogConf
ListenOn string

View File

@@ -7,6 +7,8 @@ import (
"github.com/urfave/cli"
)
// Upgrade gets the latest goctl by
// go get -u github.com/tal-tech/go-zero/tools/goctl
func Upgrade(_ *cli.Context) error {
info, err := execx.Run("GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/go-zero/tools/goctl", "")
if err != nil {

View File

@@ -28,7 +28,7 @@ type (
}
)
// NewConsole returns a instance of Console
// NewConsole returns an instance of Console
func NewConsole(idea bool) Console {
if idea {
return NewIdeaConsole()
@@ -36,7 +36,7 @@ func NewConsole(idea bool) Console {
return NewColorConsole()
}
// NewColorConsole returns a instance of colorConsole
// NewColorConsole returns an instance of colorConsole
func NewColorConsole() Console {
return &colorConsole{}
}

View File

@@ -3,6 +3,7 @@ package util
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -10,8 +11,13 @@ import (
"github.com/logrusorgru/aurora"
)
const NL = "\n"
// NL defines a new line
const (
NL = "\n"
goctlDir = ".goctl"
)
// CreateIfNotExist creates a file if it is not exists
func CreateIfNotExist(file string) (*os.File, error) {
_, err := os.Stat(file)
if !os.IsNotExist(err) {
@@ -21,6 +27,7 @@ func CreateIfNotExist(file string) (*os.File, error) {
return os.Create(file)
}
// RemoveIfExist deletes the specficed file if it is exists
func RemoveIfExist(filename string) error {
if !FileExists(filename) {
return nil
@@ -29,6 +36,7 @@ func RemoveIfExist(filename string) error {
return os.Remove(filename)
}
// RemoveOrQuit deletes the specficed file if read a permit command from stdin
func RemoveOrQuit(filename string) error {
if !FileExists(filename) {
return nil
@@ -41,11 +49,105 @@ func RemoveOrQuit(filename string) error {
return os.Remove(filename)
}
// FileExists returns true if the specficed file is exists
func FileExists(file string) bool {
_, err := os.Stat(file)
return err == nil
}
// FileNameWithoutExt returns a file name without suffix
func FileNameWithoutExt(file string) string {
return strings.TrimSuffix(file, filepath.Ext(file))
}
// GetGoctlHome returns the path value of the goctl home where Join $HOME with .goctl
func GetGoctlHome() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, goctlDir), nil
}
// GetTemplateDir returns the category path value in GoctlHome where could get it by GetGoctlHome
func GetTemplateDir(category string) (string, error) {
goctlHome, err := GetGoctlHome()
if err != nil {
return "", err
}
return filepath.Join(goctlHome, category), nil
}
// InitTemplates creates template files GoctlHome where could get it by GetGoctlHome
func InitTemplates(category string, templates map[string]string) error {
dir, err := GetTemplateDir(category)
if err != nil {
return err
}
if err := MkdirIfNotExist(dir); err != nil {
return err
}
for k, v := range templates {
if err := createTemplate(filepath.Join(dir, k), v, false); err != nil {
return err
}
}
return nil
}
// CreateTemplate writes template into file even it is exists
func CreateTemplate(category, name, content string) error {
dir, err := GetTemplateDir(category)
if err != nil {
return err
}
return createTemplate(filepath.Join(dir, name), content, true)
}
// Clean deletes all templates and removes the parent directory
func Clean(category string) error {
dir, err := GetTemplateDir(category)
if err != nil {
return err
}
return os.RemoveAll(dir)
}
// LoadTemplate gets template content by the specified file
func LoadTemplate(category, file, builtin string) (string, error) {
dir, err := GetTemplateDir(category)
if err != nil {
return "", err
}
file = filepath.Join(dir, file)
if !FileExists(file) {
return builtin, nil
}
content, err := ioutil.ReadFile(file)
if err != nil {
return "", err
}
return string(content), nil
}
func createTemplate(file, content string, force bool) error {
if FileExists(file) && !force {
return nil
}
f, err := os.Create(file)
if err != nil {
return err
}
defer f.Close()
_, err = f.WriteString(content)
return err
}

Some files were not shown because too many files have changed in this diff Show More