mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-12-29 19:45:59 +08:00
发布v2.8.4版本,更新内容请查看:https://github.com/bufanyun/hotgo/tree/v2.0/docs/guide-zh-CN/addon-version-upgrade.md
This commit is contained in:
@@ -6,6 +6,9 @@
|
||||
package hggen
|
||||
|
||||
import (
|
||||
_ "hotgo/internal/library/hggen/internal/cmd/gendao"
|
||||
_ "unsafe"
|
||||
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@@ -23,6 +26,9 @@ import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
//go:linkname doGenDaoForArray hotgo/internal/library/hggen/internal/cmd/gendao.doGenDaoForArray
|
||||
func doGenDaoForArray(ctx context.Context, index int, in gendao.CGenDaoInput)
|
||||
|
||||
// Dao 生成数据库实体
|
||||
func Dao(ctx context.Context) (err error) {
|
||||
for _, v := range daoConfig {
|
||||
@@ -31,7 +37,7 @@ func Dao(ctx context.Context) (err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
gendao.DoGenDaoForArray(ctx, inp)
|
||||
doGenDaoForArray(ctx, -1, inp)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -52,11 +58,11 @@ func ServiceWithCfg(ctx context.Context, cfg ...genservice.CGenServiceInput) (er
|
||||
}
|
||||
|
||||
// TableColumns 获取指定表生成字段列表
|
||||
func TableColumns(ctx context.Context, in sysin.GenCodesColumnListInp) (fields []*sysin.GenCodesColumnListModel, err error) {
|
||||
func TableColumns(ctx context.Context, in *sysin.GenCodesColumnListInp) (fields []*sysin.GenCodesColumnListModel, err error) {
|
||||
return views.DoTableColumns(ctx, in, GetDaoConfig(in.Name))
|
||||
}
|
||||
|
||||
func TableSelects(ctx context.Context, in sysin.GenCodesSelectsInp) (res *sysin.GenCodesSelectsModel, err error) {
|
||||
func TableSelects(ctx context.Context, in *sysin.GenCodesSelectsInp) (res *sysin.GenCodesSelectsModel, err error) {
|
||||
res = new(sysin.GenCodesSelectsModel)
|
||||
res.GenType, err = GenTypeSelect(ctx)
|
||||
if err != nil {
|
||||
@@ -110,7 +116,7 @@ func TableSelects(ctx context.Context, in sysin.GenCodesSelectsInp) (res *sysin.
|
||||
}
|
||||
sort.Sort(res.FormRole)
|
||||
|
||||
dictMode, err := service.SysDictType().TreeSelect(ctx, sysin.DictTreeSelectInp{})
|
||||
dictMode, err := service.SysDictType().TreeSelect(ctx, &sysin.DictTreeSelectInp{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -183,7 +189,7 @@ func DbSelect(ctx context.Context) (res form.Selects) {
|
||||
}
|
||||
|
||||
// Preview 生成预览
|
||||
func Preview(ctx context.Context, in sysin.GenCodesPreviewInp) (res *sysin.GenCodesPreviewModel, err error) {
|
||||
func Preview(ctx context.Context, in *sysin.GenCodesPreviewInp) (res *sysin.GenCodesPreviewModel, err error) {
|
||||
genConfig, err := service.SysConfig().GetLoadGenerate(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -209,7 +215,7 @@ func Preview(ctx context.Context, in sysin.GenCodesPreviewInp) (res *sysin.GenCo
|
||||
}
|
||||
|
||||
// Build 提交生成
|
||||
func Build(ctx context.Context, in sysin.GenCodesBuildInp) (err error) {
|
||||
func Build(ctx context.Context, in *sysin.GenCodesBuildInp) (err error) {
|
||||
genConfig, err := service.SysConfig().GetLoadGenerate(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -217,7 +223,7 @@ func Build(ctx context.Context, in sysin.GenCodesBuildInp) (err error) {
|
||||
|
||||
switch in.GenType {
|
||||
case consts.GenCodesTypeCurd:
|
||||
pin := sysin.GenCodesPreviewInp(in)
|
||||
pin := &sysin.GenCodesPreviewInp{SysGenCodes: in.SysGenCodes}
|
||||
return views.Curd.DoBuild(ctx, &views.CurdBuildInput{
|
||||
PreviewIn: &views.CurdPreviewInput{
|
||||
In: pin,
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
@@ -48,17 +55,21 @@ func (c cGF) Index(ctx context.Context, in cGFInput) (out *cGFOutput, err error)
|
||||
_, err = Version.Index(ctx, cVersionInput{})
|
||||
return
|
||||
}
|
||||
answer := "n"
|
||||
// No argument or option, do installation checks.
|
||||
if !service.Install.IsInstalled() {
|
||||
if data, isInstalled := service.Install.IsInstalled(); !isInstalled {
|
||||
mlog.Print("hi, it seams it's the first time you installing gf cli.")
|
||||
s := gcmd.Scanf("do you want to install gf binary to your system? [y/n]: ")
|
||||
if strings.EqualFold(s, "y") {
|
||||
if err = service.Install.Run(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
gcmd.Scan("press `Enter` to exit...")
|
||||
answer = gcmd.Scanf("do you want to install gf(%s) binary to your system? [y/n]: ", gf.VERSION)
|
||||
} else if !data.IsSelf {
|
||||
mlog.Print("hi, you have installed gf cli.")
|
||||
answer = gcmd.Scanf("do you want to install gf(%s) binary to your system? [y/n]: ", gf.VERSION)
|
||||
}
|
||||
if strings.EqualFold(answer, "y") {
|
||||
if err = service.Install.Run(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
gcmd.Scan("press `Enter` to exit...")
|
||||
return
|
||||
}
|
||||
// Print help content.
|
||||
gcmd.CommandFromCtx(ctx).Print()
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
@@ -123,15 +129,21 @@ type cBuildInput struct {
|
||||
VarMap g.Map `short:"r" name:"varMap" brief:"custom built embedded variable into binary"`
|
||||
PackSrc string `short:"ps" name:"packSrc" brief:"pack one or more folders into one go file before building"`
|
||||
PackDst string `short:"pd" name:"packDst" brief:"temporary go file path for pack, this go file will be automatically removed after built" d:"internal/packed/build_pack_data.go"`
|
||||
ExitWhenError bool `short:"ew" name:"exitWhenError" brief:"exit building when any error occurs, default is false" orphan:"true"`
|
||||
ExitWhenError bool `short:"ew" name:"exitWhenError" brief:"exit building when any error occurs, specially for multiple arch and system buildings. default is false" orphan:"true"`
|
||||
DumpENV bool `short:"de" name:"dumpEnv" brief:"dump current go build environment before building binary" orphan:"true"`
|
||||
}
|
||||
|
||||
type cBuildOutput struct{}
|
||||
|
||||
func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, err error) {
|
||||
// print used go env
|
||||
if in.DumpENV {
|
||||
_, _ = Env.Index(ctx, cEnvInput{})
|
||||
}
|
||||
|
||||
mlog.SetHeaderPrint(true)
|
||||
|
||||
mlog.Debugf(`build input: %+v`, in)
|
||||
mlog.Debugf(`build command input: %+v`, in)
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("go") == "" {
|
||||
mlog.Fatalf(`command "go" not found in your environment, please install golang first to proceed this command`)
|
||||
@@ -236,7 +248,7 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
|
||||
if len(customSystems) > 0 && customSystems[0] != "all" && !gstr.InArray(customSystems, system) {
|
||||
continue
|
||||
}
|
||||
for arch, _ := range item {
|
||||
for arch := range item {
|
||||
if len(customArches) > 0 && customArches[0] != "all" && !gstr.InArray(customArches, arch) {
|
||||
continue
|
||||
}
|
||||
@@ -299,8 +311,9 @@ func (c cBuild) getBuildInVarStr(ctx context.Context, in cBuildInput) string {
|
||||
if buildInVarMap == nil {
|
||||
buildInVarMap = make(g.Map)
|
||||
}
|
||||
buildInVarMap["builtGit"] = c.getGitCommit(ctx)
|
||||
buildInVarMap["builtTime"] = gtime.Now().String()
|
||||
buildInVarMap[`builtGit`] = c.getGitCommit(ctx)
|
||||
buildInVarMap[`builtTime`] = gtime.Now().String()
|
||||
buildInVarMap[`builtVersion`] = in.Version
|
||||
b, err := json.Marshal(buildInVarMap)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
@@ -33,6 +39,7 @@ gf docker main.go
|
||||
gf docker main.go -t hub.docker.com/john/image:tag
|
||||
gf docker main.go -t hub.docker.com/john/image:tag
|
||||
gf docker main.go -p -t hub.docker.com/john/image:tag
|
||||
gf docker main.go -p -tp ["hub.docker.com/john","hub.docker.com/smith"] -tn image:tag
|
||||
`
|
||||
cDockerDc = `
|
||||
The "docker" command builds the GF project to a docker images.
|
||||
@@ -45,6 +52,7 @@ You should have docker installed, and there must be a Dockerfile in the root of
|
||||
cDockerFileBrief = `file path of the Dockerfile. it's "manifest/docker/Dockerfile" in default`
|
||||
cDockerShellBrief = `path of the shell file which is executed before docker build`
|
||||
cDockerPushBrief = `auto push the docker image to docker registry if "-t" option passed`
|
||||
cDockerTagBrief = `full tag for this docker, pattern like "xxx.xxx.xxx/image:tag"`
|
||||
cDockerTagNameBrief = `tag name for this docker, pattern like "image:tag". this option is required with TagPrefixes`
|
||||
cDockerTagPrefixesBrief = `tag prefixes for this docker, which are used for docker push. this option is required with TagName`
|
||||
cDockerExtraBrief = `extra build options passed to "docker image"`
|
||||
@@ -61,6 +69,7 @@ func init() {
|
||||
`cDockerShellBrief`: cDockerShellBrief,
|
||||
`cDockerBuildBrief`: cDockerBuildBrief,
|
||||
`cDockerPushBrief`: cDockerPushBrief,
|
||||
`cDockerTagBrief`: cDockerTagBrief,
|
||||
`cDockerTagNameBrief`: cDockerTagNameBrief,
|
||||
`cDockerTagPrefixesBrief`: cDockerTagPrefixesBrief,
|
||||
`cDockerExtraBrief`: cDockerExtraBrief,
|
||||
@@ -69,10 +78,11 @@ func init() {
|
||||
|
||||
type cDockerInput struct {
|
||||
g.Meta `name:"docker" config:"gfcli.docker"`
|
||||
Main string `name:"MAIN" arg:"true" brief:"{cDockerMainBrief}" d:"main.go"`
|
||||
Main string `name:"MAIN" arg:"true" brief:"{cDockerMainBrief}" d:"main.go"`
|
||||
File string `name:"file" short:"f" brief:"{cDockerFileBrief}" d:"manifest/docker/Dockerfile"`
|
||||
Shell string `name:"shell" short:"s" brief:"{cDockerShellBrief}" d:"manifest/docker/docker.sh"`
|
||||
Build string `name:"build" short:"b" brief:"{cDockerBuildBrief}" d:"-a amd64 -s linux"`
|
||||
Build string `name:"build" short:"b" brief:"{cDockerBuildBrief}"`
|
||||
Tag string `name:"tag" short:"t" brief:"{cDockerTagBrief}"`
|
||||
TagName string `name:"tagName" short:"tn" brief:"{cDockerTagNameBrief}" v:"required-with:TagPrefixes"`
|
||||
TagPrefixes []string `name:"tagPrefixes" short:"tp" brief:"{cDockerTagPrefixesBrief}" v:"required-with:TagName"`
|
||||
Push bool `name:"push" short:"p" brief:"{cDockerPushBrief}" orphan:"true"`
|
||||
@@ -87,17 +97,23 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
|
||||
mlog.Fatalf(`command "docker" not found in your environment, please install docker first to proceed this command`)
|
||||
}
|
||||
|
||||
mlog.Debugf(`docker command input: %+v`, in)
|
||||
|
||||
// Binary build.
|
||||
in.Build += " --exit"
|
||||
if in.Main != "" {
|
||||
if err = gproc.ShellRun(ctx, fmt.Sprintf(`gf build %s %s`, in.Main, in.Build)); err != nil {
|
||||
return
|
||||
if in.Main != "" && in.Build != "" {
|
||||
in.Build += " --exitWhenError"
|
||||
if in.Main != "" {
|
||||
if err = gproc.ShellRun(ctx, fmt.Sprintf(`gf build %s %s`, in.Main, in.Build)); err != nil {
|
||||
mlog.Debugf(`build binary failed with error: %+v`, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shell executing.
|
||||
if in.Shell != "" && gfile.Exists(in.Shell) {
|
||||
if err = c.exeDockerShell(ctx, in.Shell); err != nil {
|
||||
mlog.Debugf(`build docker failed with error: %+v`, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -114,7 +130,7 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
|
||||
}
|
||||
}
|
||||
if len(dockerTags) == 0 {
|
||||
dockerTags = []string{""}
|
||||
dockerTags = []string{in.Tag}
|
||||
}
|
||||
for i, dockerTag := range dockerTags {
|
||||
if i > 0 {
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
@@ -19,7 +28,9 @@ type cFix struct {
|
||||
}
|
||||
|
||||
type cFixInput struct {
|
||||
g.Meta `name:"fix"`
|
||||
g.Meta `name:"fix"`
|
||||
Path string `name:"path" short:"p" brief:"directory path, it uses current working directory in default"`
|
||||
Version string `name:"version" short:"v" brief:"custom specified version to fix, leave it empty to auto detect"`
|
||||
}
|
||||
|
||||
type cFixOutput struct{}
|
||||
@@ -30,35 +41,46 @@ type cFixItem struct {
|
||||
}
|
||||
|
||||
func (c cFix) Index(ctx context.Context, in cFixInput) (out *cFixOutput, err error) {
|
||||
mlog.Print(`start auto fixing...`)
|
||||
defer mlog.Print(`done!`)
|
||||
err = c.doFix()
|
||||
|
||||
if in.Path == "" {
|
||||
in.Path = gfile.Pwd()
|
||||
}
|
||||
if in.Version == "" {
|
||||
in.Version, err = c.autoDetectVersion(in)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if in.Version == "" {
|
||||
mlog.Print(`no GoFrame usage found, exit fixing`)
|
||||
return
|
||||
}
|
||||
mlog.Debugf(`current GoFrame version auto detect "%s"`, in.Version)
|
||||
}
|
||||
|
||||
if !gproc.IsChild() {
|
||||
mlog.Printf(`start auto fixing directory path "%s"...`, in.Path)
|
||||
defer mlog.Print(`done!`)
|
||||
}
|
||||
|
||||
err = c.doFix(in)
|
||||
return
|
||||
}
|
||||
|
||||
func (c cFix) doFix() (err error) {
|
||||
version, err := c.getVersion()
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if version == "" {
|
||||
mlog.Print(`no GoFrame usage found, exit fixing`)
|
||||
return
|
||||
}
|
||||
mlog.Debugf(`current GoFrame version found "%s"`, version)
|
||||
func (c cFix) doFix(in cFixInput) (err error) {
|
||||
|
||||
var items = []cFixItem{
|
||||
{Version: "v2.3", Func: c.doFixV23},
|
||||
{Version: "v2.5", Func: c.doFixV25},
|
||||
}
|
||||
for _, item := range items {
|
||||
if gstr.CompareVersionGo(version, item.Version) < 0 {
|
||||
if gstr.CompareVersionGo(in.Version, item.Version) < 0 {
|
||||
mlog.Debugf(
|
||||
`current GoFrame version "%s" is lesser than "%s", nothing to do`,
|
||||
version, item.Version,
|
||||
`current GoFrame or contrib package version "%s" is lesser than "%s", nothing to do`,
|
||||
in.Version, item.Version,
|
||||
)
|
||||
continue
|
||||
}
|
||||
if err = item.Func(version); err != nil {
|
||||
if err = item.Func(in.Version); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -68,16 +90,47 @@ func (c cFix) doFix() (err error) {
|
||||
// doFixV23 fixes code when upgrading to GoFrame v2.3.
|
||||
func (c cFix) doFixV23(version string) error {
|
||||
replaceFunc := func(path, content string) string {
|
||||
// gdb.TX from struct to interface.
|
||||
content = gstr.Replace(content, "*gdb.TX", "gdb.TX")
|
||||
// function name changes for package gtcp/gudp.
|
||||
if gstr.Contains(content, "/gf/v2/net/gtcp") || gstr.Contains(content, "/gf/v2/net/gudp") {
|
||||
content = gstr.ReplaceByMap(content, g.MapStrStr{
|
||||
".SetSendDeadline": ".SetDeadlineSend",
|
||||
".SetReceiveDeadline": ".SetDeadlineRecv",
|
||||
".SetReceiveBufferWait": ".SetBufferWaitRecv",
|
||||
})
|
||||
}
|
||||
return content
|
||||
}
|
||||
return gfile.ReplaceDirFunc(replaceFunc, ".", "*.go", true)
|
||||
}
|
||||
|
||||
func (c cFix) getVersion() (string, error) {
|
||||
// doFixV25 fixes code when upgrading to GoFrame v2.5.
|
||||
func (c cFix) doFixV25(version string) (err error) {
|
||||
replaceFunc := func(path, content string) string {
|
||||
content, err = c.doFixV25Content(content)
|
||||
return content
|
||||
}
|
||||
return gfile.ReplaceDirFunc(replaceFunc, ".", "*.go", true)
|
||||
}
|
||||
|
||||
func (c cFix) doFixV25Content(content string) (newContent string, err error) {
|
||||
newContent = content
|
||||
if gstr.Contains(content, `.BindHookHandlerByMap(`) {
|
||||
var pattern = `\.BindHookHandlerByMap\((.+?), map\[string\]ghttp\.HandlerFunc`
|
||||
newContent, err = gregex.ReplaceString(
|
||||
pattern,
|
||||
`.BindHookHandlerByMap($1, map[ghttp.HookName]ghttp.HandlerFunc`,
|
||||
content,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c cFix) autoDetectVersion(in cFixInput) (string, error) {
|
||||
var (
|
||||
err error
|
||||
path = "go.mod"
|
||||
path = gfile.Join(in.Path, "go.mod")
|
||||
version string
|
||||
)
|
||||
if !gfile.Exists(path) {
|
||||
@@ -86,7 +139,7 @@ func (c cFix) getVersion() (string, error) {
|
||||
err = gfile.ReadLines(path, func(line string) error {
|
||||
array := gstr.SplitAndTrim(line, " ")
|
||||
if len(array) > 0 {
|
||||
if array[0] == gfPackage {
|
||||
if gstr.HasPrefix(array[0], gfPackage) {
|
||||
version = array[1]
|
||||
}
|
||||
}
|
||||
|
||||
20
server/internal/library/hggen/internal/cmd/cmd_fix_test.go
Normal file
20
server/internal/library/hggen/internal/cmd/cmd_fix_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Fix_doFixV25Content(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
content = gtest.DataContent(`fix25_content.go.txt`)
|
||||
f = cFix{}
|
||||
)
|
||||
newContent, err := f.doFixV25Content(content)
|
||||
t.AssertNil(err)
|
||||
fmt.Println(newContent)
|
||||
})
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
@@ -13,6 +19,7 @@ type cGen struct {
|
||||
g.Meta `name:"gen" brief:"{cGenBrief}" dc:"{cGenDc}"`
|
||||
cGenDao
|
||||
cGenEnums
|
||||
cGenCtrl
|
||||
cGenPb
|
||||
cGenPbEntity
|
||||
cGenService
|
||||
|
||||
15
server/internal/library/hggen/internal/cmd/cmd_gen_ctrl.go
Normal file
15
server/internal/library/hggen/internal/cmd/cmd_gen_ctrl.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"hotgo/internal/library/hggen/internal/cmd/genctrl"
|
||||
)
|
||||
|
||||
type (
|
||||
cGenCtrl = genctrl.CGenCtrl
|
||||
)
|
||||
@@ -1,8 +1,15 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
//_ "github.com/gogf/gf/contrib/drivers/clickhouse/v2"
|
||||
//_ "github.com/gogf/gf/contrib/drivers/mssql/v2"
|
||||
//_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||
//_ "github.com/gogf/gf/contrib/drivers/oracle/v2"
|
||||
//_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
//_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
|
||||
@@ -1,79 +1,13 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
import "hotgo/internal/library/hggen/internal/cmd/genpb"
|
||||
|
||||
type (
|
||||
cGenPb struct{}
|
||||
cGenPbInput struct {
|
||||
g.Meta `name:"pb" brief:"parse proto files and generate protobuf go files"`
|
||||
}
|
||||
cGenPbOutput struct{}
|
||||
cGenPb = genpb.CGenPb
|
||||
)
|
||||
|
||||
func (c cGenPb) Pb(ctx context.Context, in cGenPbInput) (out *cGenPbOutput, err error) {
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("protoc") == "" {
|
||||
mlog.Fatalf(`command "protoc" not found in your environment, please install protoc first to proceed this command`)
|
||||
}
|
||||
|
||||
// protocol fold checks.
|
||||
protoFolder := "protocol"
|
||||
if !gfile.Exists(protoFolder) {
|
||||
mlog.Fatalf(`proto files folder "%s" does not exist`, protoFolder)
|
||||
}
|
||||
// folder scanning.
|
||||
files, err := gfile.ScanDirFile(protoFolder, "*.proto", true)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if len(files) == 0 {
|
||||
mlog.Fatalf(`no proto files found in folder "%s"`, protoFolder)
|
||||
}
|
||||
dirSet := gset.NewStrSet()
|
||||
for _, file := range files {
|
||||
dirSet.Add(gfile.Dir(file))
|
||||
}
|
||||
var (
|
||||
servicePath = gfile.RealPath(".")
|
||||
goPathSrc = gfile.RealPath(gfile.Join(genv.Get("GOPATH").String(), "src"))
|
||||
)
|
||||
dirSet.Iterator(func(protoDirPath string) bool {
|
||||
parsingCommand := fmt.Sprintf(
|
||||
"protoc --gofast_out=plugins=grpc:. %s/*.proto -I%s",
|
||||
protoDirPath,
|
||||
servicePath,
|
||||
)
|
||||
if goPathSrc != "" {
|
||||
parsingCommand += " -I" + goPathSrc
|
||||
}
|
||||
mlog.Print(parsingCommand)
|
||||
if output, err := gproc.ShellExec(ctx, parsingCommand); err != nil {
|
||||
mlog.Print(output)
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
return true
|
||||
})
|
||||
// Custom replacement.
|
||||
//pbFolder := "protobuf"
|
||||
//_, _ = gfile.ScanDirFileFunc(pbFolder, "*.go", true, func(path string) string {
|
||||
// content := gfile.GetContents(path)
|
||||
// content = gstr.ReplaceByArray(content, g.SliceStr{
|
||||
// `gtime "gtime"`, `gtime "github.com/gogf/gf/v2/os/gtime"`,
|
||||
// })
|
||||
// _ = gfile.PutContents(path, content)
|
||||
// utils.GoFmt(path)
|
||||
// return path
|
||||
//})
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,411 +1,13 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/consts"
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
import "hotgo/internal/library/hggen/internal/cmd/genpbentity"
|
||||
|
||||
type (
|
||||
cGenPbEntity struct{}
|
||||
cGenPbEntityInput struct {
|
||||
g.Meta `name:"pbentity" config:"{cGenPbEntityConfig}" brief:"{cGenPbEntityBrief}" eg:"{cGenPbEntityEg}" ad:"{cGenPbEntityAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{cGenPbEntityBriefPath}"`
|
||||
Package string `name:"package" short:"k" brief:"{cGenPbEntityBriefPackage}"`
|
||||
Link string `name:"link" short:"l" brief:"{cGenPbEntityBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{cGenPbEntityBriefTables}"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{cGenPbEntityBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenPbEntityBriefRemovePrefix}"`
|
||||
NameCase string `name:"nameCase" short:"n" brief:"{cGenPbEntityBriefNameCase}" d:"Camel"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenPbEntityBriefJsonCase}" d:"CamelLower"`
|
||||
Option string `name:"option" short:"o" brief:"{cGenPbEntityBriefOption}"`
|
||||
}
|
||||
cGenPbEntityOutput struct{}
|
||||
|
||||
cGenPbEntityInternalInput struct {
|
||||
cGenPbEntityInput
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
}
|
||||
cGenPbEntity = genpbentity.CGenPbEntity
|
||||
)
|
||||
|
||||
const (
|
||||
cGenPbEntityConfig = `gfcli.gen.pbentity`
|
||||
cGenPbEntityBrief = `generate entity message files in protobuf3 format`
|
||||
cGenPbEntityEg = `
|
||||
gf gen pbentity
|
||||
gf gen pbentity -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
gf gen pbentity -p ./protocol/demos/entity -t user,user_detail,user_login
|
||||
gf gen pbentity -r user_
|
||||
`
|
||||
|
||||
cGenPbEntityAd = `
|
||||
CONFIGURATION SUPPORT
|
||||
Options are also supported by configuration file.
|
||||
It's suggested using configuration file instead of command line arguments making producing.
|
||||
The configuration node name is "gf.gen.pbentity", which also supports multiple databases, for example(config.yaml):
|
||||
gfcli:
|
||||
gen:
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
path: "protocol/demos/entity"
|
||||
tables: "order,products"
|
||||
package: "demos"
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
|
||||
path: "protocol/demos/entity"
|
||||
prefix: "primary_"
|
||||
tables: "user, userDetail"
|
||||
package: "demos"
|
||||
option: |
|
||||
option go_package = "protobuf/demos";
|
||||
option java_package = "protobuf/demos";
|
||||
option php_namespace = "protobuf/demos";
|
||||
`
|
||||
cGenPbEntityBriefPath = `directory path for generated files`
|
||||
cGenPbEntityBriefPackage = `package name for all entity proto files`
|
||||
cGenPbEntityBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
cGenPbEntityBriefTables = `generate models only for given tables, multiple table names separated with ','`
|
||||
cGenPbEntityBriefPrefix = `add specified prefix for all entity names and entity proto files`
|
||||
cGenPbEntityBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
|
||||
cGenPbEntityBriefOption = `extra protobuf options`
|
||||
cGenPbEntityBriefGroup = `
|
||||
specifying the configuration group name of database for generated ORM instance,
|
||||
it's not necessary and the default value is "default"
|
||||
`
|
||||
|
||||
cGenPbEntityBriefNameCase = `
|
||||
case for message attribute names, default is "Camel":
|
||||
| Case | Example |
|
||||
|---------------- |--------------------|
|
||||
| Camel | AnyKindOfString |
|
||||
| CamelLower | anyKindOfString | default
|
||||
| Snake | any_kind_of_string |
|
||||
| SnakeScreaming | ANY_KIND_OF_STRING |
|
||||
| SnakeFirstUpper | rgb_code_md5 |
|
||||
| Kebab | any-kind-of-string |
|
||||
| KebabScreaming | ANY-KIND-OF-STRING |
|
||||
`
|
||||
|
||||
cGenPbEntityBriefJsonCase = `
|
||||
case for message json tag, cases are the same as "nameCase", default "CamelLower".
|
||||
set it to "none" to ignore json tag generating.
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGenPbEntityConfig`: cGenPbEntityConfig,
|
||||
`cGenPbEntityBrief`: cGenPbEntityBrief,
|
||||
`cGenPbEntityEg`: cGenPbEntityEg,
|
||||
`cGenPbEntityAd`: cGenPbEntityAd,
|
||||
`cGenPbEntityBriefPath`: cGenPbEntityBriefPath,
|
||||
`cGenPbEntityBriefPackage`: cGenPbEntityBriefPackage,
|
||||
`cGenPbEntityBriefLink`: cGenPbEntityBriefLink,
|
||||
`cGenPbEntityBriefTables`: cGenPbEntityBriefTables,
|
||||
`cGenPbEntityBriefPrefix`: cGenPbEntityBriefPrefix,
|
||||
`cGenPbEntityBriefRemovePrefix`: cGenPbEntityBriefRemovePrefix,
|
||||
`cGenPbEntityBriefGroup`: cGenPbEntityBriefGroup,
|
||||
`cGenPbEntityBriefNameCase`: cGenPbEntityBriefNameCase,
|
||||
`cGenPbEntityBriefJsonCase`: cGenPbEntityBriefJsonCase,
|
||||
`cGenPbEntityBriefOption`: cGenPbEntityBriefOption,
|
||||
})
|
||||
}
|
||||
|
||||
func (c cGenPbEntity) PbEntity(ctx context.Context, in cGenPbEntityInput) (out *cGenPbEntityOutput, err error) {
|
||||
var (
|
||||
config = g.Cfg()
|
||||
)
|
||||
if config.Available(ctx) {
|
||||
v := config.MustGet(ctx, cGenPbEntityConfig)
|
||||
if v.IsSlice() {
|
||||
for i := 0; i < len(v.Interfaces()); i++ {
|
||||
doGenPbEntityForArray(ctx, i, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
func doGenPbEntityForArray(ctx context.Context, index int, in cGenPbEntityInput) {
|
||||
var (
|
||||
err error
|
||||
db gdb.DB
|
||||
)
|
||||
if index >= 0 {
|
||||
err = g.Cfg().MustGet(
|
||||
ctx,
|
||||
fmt.Sprintf(`%s.%d`, cGenPbEntityConfig, index),
|
||||
).Scan(&in)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`invalid configuration of "%s": %+v`, cGenPbEntityConfig, err)
|
||||
}
|
||||
}
|
||||
if in.Package == "" {
|
||||
mlog.Fatal("package name should not be empty")
|
||||
}
|
||||
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
|
||||
// It uses user passed database configuration.
|
||||
if in.Link != "" {
|
||||
var (
|
||||
tempGroup = gtime.TimestampNanoStr()
|
||||
match, _ = gregex.MatchString(`([a-z]+):(.+)`, in.Link)
|
||||
)
|
||||
if len(match) == 3 {
|
||||
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
|
||||
Type: gstr.Trim(match[1]),
|
||||
Link: gstr.Trim(match[2]),
|
||||
})
|
||||
db, _ = gdb.Instance(tempGroup)
|
||||
}
|
||||
} else {
|
||||
db = g.DB()
|
||||
}
|
||||
if db == nil {
|
||||
mlog.Fatal("database initialization failed")
|
||||
}
|
||||
|
||||
tableNames := ([]string)(nil)
|
||||
if in.Tables != "" {
|
||||
tableNames = gstr.SplitAndTrim(in.Tables, ",")
|
||||
} else {
|
||||
tableNames, err = db.Tables(context.TODO())
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables failed: \n %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tableName := range tableNames {
|
||||
newTableName := tableName
|
||||
for _, v := range removePrefixArray {
|
||||
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
|
||||
}
|
||||
generatePbEntityContentFile(ctx, db, cGenPbEntityInternalInput{
|
||||
cGenPbEntityInput: in,
|
||||
TableName: tableName,
|
||||
NewTableName: newTableName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// generatePbEntityContentFile generates the protobuf files for given table.
|
||||
func generatePbEntityContentFile(ctx context.Context, db gdb.DB, in cGenPbEntityInternalInput) {
|
||||
fieldMap, err := db.TableFields(ctx, in.TableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
// Change the `newTableName` if `Prefix` is given.
|
||||
newTableName := "Entity_" + in.Prefix + in.NewTableName
|
||||
var (
|
||||
tableNameCamelCase = gstr.CaseCamel(newTableName)
|
||||
tableNameSnakeCase = gstr.CaseSnake(newTableName)
|
||||
entityMessageDefine = generateEntityMessageDefinition(tableNameCamelCase, fieldMap, in)
|
||||
fileName = gstr.Trim(tableNameSnakeCase, "-_.")
|
||||
path = gfile.Join(in.Path, fileName+".proto")
|
||||
)
|
||||
entityContent := gstr.ReplaceByMap(getTplPbEntityContent(""), g.MapStrStr{
|
||||
"{PackageName}": in.Package,
|
||||
"{OptionContent}": in.Option,
|
||||
"{EntityMessage}": entityMessageDefine,
|
||||
})
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(entityContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
|
||||
// generateEntityMessageDefinition generates and returns the message definition for specified table.
|
||||
func generateEntityMessageDefinition(entityName string, fieldMap map[string]*gdb.TableField, in cGenPbEntityInternalInput) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForPbEntity(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
array[index] = generateMessageFieldForPbEntity(index+1, fieldMap[name], in)
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
stContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
stContent = gstr.Replace(stContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(fmt.Sprintf("message %s {\n", entityName))
|
||||
buffer.WriteString(stContent)
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateMessageFieldForPbEntity generates and returns the message definition for specified field.
|
||||
func generateMessageFieldForPbEntity(index int, field *gdb.TableField, in cGenPbEntityInternalInput) []string {
|
||||
var (
|
||||
typeName string
|
||||
comment string
|
||||
jsonTagStr string
|
||||
)
|
||||
t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type)
|
||||
t = gstr.Split(gstr.Trim(t), " ")[0]
|
||||
t = gstr.ToLower(t)
|
||||
switch t {
|
||||
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
|
||||
typeName = "bytes"
|
||||
|
||||
case "bit", "int", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint32"
|
||||
} else {
|
||||
typeName = "int32"
|
||||
}
|
||||
|
||||
case "int8", "big_int", "bigint", "bigserial":
|
||||
if gstr.ContainsI(field.Type, "unsigned") {
|
||||
typeName = "uint64"
|
||||
} else {
|
||||
typeName = "int64"
|
||||
}
|
||||
|
||||
case "real":
|
||||
typeName = "float"
|
||||
|
||||
case "float", "double", "decimal", "smallmoney":
|
||||
typeName = "double"
|
||||
|
||||
case "bool":
|
||||
typeName = "bool"
|
||||
|
||||
case "datetime", "timestamp", "date", "time":
|
||||
typeName = "int64"
|
||||
|
||||
default:
|
||||
// Auto detecting type.
|
||||
switch {
|
||||
case strings.Contains(t, "int"):
|
||||
typeName = "int"
|
||||
case strings.Contains(t, "text") || strings.Contains(t, "char"):
|
||||
typeName = "string"
|
||||
case strings.Contains(t, "float") || strings.Contains(t, "double"):
|
||||
typeName = "double"
|
||||
case strings.Contains(t, "bool"):
|
||||
typeName = "bool"
|
||||
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
|
||||
typeName = "bytes"
|
||||
case strings.Contains(t, "date") || strings.Contains(t, "time"):
|
||||
typeName = "int64"
|
||||
default:
|
||||
typeName = "string"
|
||||
}
|
||||
}
|
||||
comment = gstr.ReplaceByArray(field.Comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
})
|
||||
comment = gstr.Trim(comment)
|
||||
comment = gstr.Replace(comment, `\n`, " ")
|
||||
comment, _ = gregex.ReplaceString(`\s{2,}`, ` `, comment)
|
||||
if jsonTagName := formatCase(field.Name, in.JsonCase); jsonTagName != "" {
|
||||
jsonTagStr = fmt.Sprintf(`[(gogoproto.jsontag) = "%s"]`, jsonTagName)
|
||||
// beautiful indent.
|
||||
if index < 10 {
|
||||
// 3 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else if index < 100 {
|
||||
// 2 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else {
|
||||
// 1 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
}
|
||||
}
|
||||
return []string{
|
||||
" #" + typeName,
|
||||
" #" + formatCase(field.Name, in.NameCase),
|
||||
" #= " + gconv.String(index) + jsonTagStr + ";",
|
||||
" #" + fmt.Sprintf(`// %s`, comment),
|
||||
}
|
||||
}
|
||||
|
||||
func getTplPbEntityContent(tplEntityPath string) string {
|
||||
if tplEntityPath != "" {
|
||||
return gfile.GetContents(tplEntityPath)
|
||||
}
|
||||
return consts.TemplatePbEntityMessageContent
|
||||
}
|
||||
|
||||
// formatCase call gstr.Case* function to convert the s to specified case.
|
||||
func formatCase(str, caseStr string) string {
|
||||
switch gstr.ToLower(caseStr) {
|
||||
case gstr.ToLower("Camel"):
|
||||
return gstr.CaseCamel(str)
|
||||
|
||||
case gstr.ToLower("CamelLower"):
|
||||
return gstr.CaseCamelLower(str)
|
||||
|
||||
case gstr.ToLower("Kebab"):
|
||||
return gstr.CaseKebab(str)
|
||||
|
||||
case gstr.ToLower("KebabScreaming"):
|
||||
return gstr.CaseKebabScreaming(str)
|
||||
|
||||
case gstr.ToLower("Snake"):
|
||||
return gstr.CaseSnake(str)
|
||||
|
||||
case gstr.ToLower("SnakeFirstUpper"):
|
||||
return gstr.CaseSnakeFirstUpper(str)
|
||||
|
||||
case gstr.ToLower("SnakeScreaming"):
|
||||
return gstr.CaseSnakeScreaming(str)
|
||||
|
||||
case "none":
|
||||
return ""
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func sortFieldKeyForPbEntity(fieldMap map[string]*gdb.TableField) []string {
|
||||
names := make(map[int]string)
|
||||
for _, field := range fieldMap {
|
||||
names[field.Index] = field.Name
|
||||
}
|
||||
var (
|
||||
result = make([]string, len(names))
|
||||
i = 0
|
||||
j = 0
|
||||
)
|
||||
for {
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
if val, ok := names[i]; ok {
|
||||
result[j] = val
|
||||
j++
|
||||
delete(names, i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@@ -12,6 +17,7 @@ import (
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/utility/allyes"
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gtype"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@@ -154,7 +161,7 @@ func (app *cRunApp) Run(ctx context.Context) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// Special handling for windows platform.
|
||||
// DO NOT USE "cmd /c" command.
|
||||
process = gproc.NewProcess(runCommand, nil)
|
||||
process = gproc.NewProcess(outputPath, strings.Fields(app.Args))
|
||||
} else {
|
||||
process = gproc.NewProcessCmd(runCommand, nil)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/minio/selfupdate"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
"hotgo/internal/library/hggen/internal/utility/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -21,12 +32,12 @@ type cUp struct {
|
||||
}
|
||||
|
||||
const (
|
||||
gfPackage = `github.com/gogf/gf/v2`
|
||||
gfPackage = `github.com/gogf/gf/`
|
||||
cUpEg = `
|
||||
gf up
|
||||
gf up -a
|
||||
gf up -c
|
||||
gf up -f -c
|
||||
gf up -cf
|
||||
`
|
||||
)
|
||||
|
||||
@@ -39,51 +50,90 @@ func init() {
|
||||
type cUpInput struct {
|
||||
g.Meta `name:"up" config:"gfcli.up"`
|
||||
All bool `name:"all" short:"a" brief:"upgrade both version and cli, auto fix codes" orphan:"true"`
|
||||
Fix bool `name:"fix" short:"f" brief:"auto fix codes" orphan:"true"`
|
||||
Cli bool `name:"cli" short:"c" brief:"also upgrade CLI tool (not supported yet)" orphan:"true"`
|
||||
Cli bool `name:"cli" short:"c" brief:"also upgrade CLI tool" orphan:"true"`
|
||||
Fix bool `name:"fix" short:"f" brief:"auto fix codes(it only make sense if cli is to be upgraded)" orphan:"true"`
|
||||
}
|
||||
|
||||
type cUpOutput struct{}
|
||||
|
||||
func (c cUp) Index(ctx context.Context, in cUpInput) (out *cUpOutput, err error) {
|
||||
defer mlog.Print(`done!`)
|
||||
defer func() {
|
||||
if err == nil {
|
||||
mlog.Print()
|
||||
mlog.Print(`👏congratulations! you've upgraded to the latest version of GoFrame! enjoy it!👏`)
|
||||
mlog.Print()
|
||||
}
|
||||
}()
|
||||
|
||||
var doUpgradeVersionOut *doUpgradeVersionOutput
|
||||
if in.All {
|
||||
in.Cli = true
|
||||
in.Fix = true
|
||||
}
|
||||
if err = c.doUpgradeVersion(ctx); err != nil {
|
||||
if doUpgradeVersionOut, err = c.doUpgradeVersion(ctx, in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if in.Fix {
|
||||
if err = c.doAutoFixing(ctx); err != nil {
|
||||
|
||||
if in.Cli {
|
||||
if err = c.doUpgradeCLI(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
//if in.Cli {
|
||||
// if err = c.doUpgradeCLI(ctx); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//}
|
||||
|
||||
if in.Cli && in.Fix {
|
||||
if doUpgradeVersionOut != nil && len(doUpgradeVersionOut.Items) > 0 {
|
||||
upgradedPathSet := gset.NewStrSet()
|
||||
for _, item := range doUpgradeVersionOut.Items {
|
||||
if !upgradedPathSet.AddIfNotExist(item.DirPath) {
|
||||
continue
|
||||
}
|
||||
if err = c.doAutoFixing(ctx, item.DirPath, item.Version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c cUp) doUpgradeVersion(ctx context.Context) (err error) {
|
||||
type doUpgradeVersionOutput struct {
|
||||
Items []doUpgradeVersionOutputItem
|
||||
}
|
||||
|
||||
type doUpgradeVersionOutputItem struct {
|
||||
DirPath string
|
||||
Version string
|
||||
}
|
||||
|
||||
func (c cUp) doUpgradeVersion(ctx context.Context, in cUpInput) (out *doUpgradeVersionOutput, err error) {
|
||||
mlog.Print(`start upgrading version...`)
|
||||
out = &doUpgradeVersionOutput{
|
||||
Items: make([]doUpgradeVersionOutputItem, 0),
|
||||
}
|
||||
type Package struct {
|
||||
Name string
|
||||
Version string
|
||||
}
|
||||
|
||||
var (
|
||||
dir = gfile.Pwd()
|
||||
temp string
|
||||
path = gfile.Join(dir, "go.mod")
|
||||
temp string
|
||||
dirPath = gfile.Pwd()
|
||||
goModPath = gfile.Join(dirPath, "go.mod")
|
||||
)
|
||||
// It recursively upgrades the go.mod from sub folder to its parent folders.
|
||||
for {
|
||||
if gfile.Exists(path) {
|
||||
var packages []string
|
||||
err = gfile.ReadLines(path, func(line string) error {
|
||||
if gfile.Exists(goModPath) {
|
||||
var packages []Package
|
||||
err = gfile.ReadLines(goModPath, func(line string) error {
|
||||
line = gstr.Trim(line)
|
||||
line = gstr.TrimLeftStr(line, "require ")
|
||||
line = gstr.Trim(line)
|
||||
if gstr.HasPrefix(line, gfPackage) {
|
||||
pkg := gstr.Explode(" ", line)[0]
|
||||
packages = append(packages, pkg)
|
||||
array := gstr.SplitAndTrim(line, " ")
|
||||
packages = append(packages, Package{
|
||||
Name: array[0],
|
||||
Version: array[1],
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -91,31 +141,79 @@ func (c cUp) doUpgradeVersion(ctx context.Context) (err error) {
|
||||
return
|
||||
}
|
||||
for _, pkg := range packages {
|
||||
mlog.Printf(`upgrading %s`, pkg)
|
||||
command := fmt.Sprintf(`go get -u %s@latest`, pkg)
|
||||
mlog.Printf(`upgrading "%s" from "%s" to "latest"`, pkg.Name, pkg.Version)
|
||||
// go get -u
|
||||
command := fmt.Sprintf(`cd %s && go get -u %s@latest`, dirPath, pkg.Name)
|
||||
if err = gproc.ShellRun(ctx, command); err != nil {
|
||||
return
|
||||
}
|
||||
// go mod tidy
|
||||
if err = utils.GoModTidy(ctx, dirPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out.Items = append(out.Items, doUpgradeVersionOutputItem{
|
||||
DirPath: dirPath,
|
||||
Version: pkg.Version,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
temp = gfile.Dir(dir)
|
||||
if temp == "" || temp == dir {
|
||||
temp = gfile.Dir(dirPath)
|
||||
if temp == "" || temp == dirPath {
|
||||
return
|
||||
}
|
||||
dir = temp
|
||||
path = gfile.Join(dir, "go.mod")
|
||||
dirPath = temp
|
||||
goModPath = gfile.Join(dirPath, "go.mod")
|
||||
}
|
||||
}
|
||||
|
||||
// doUpgradeCLI downloads the new version binary with process.
|
||||
func (c cUp) doUpgradeCLI(ctx context.Context) (err error) {
|
||||
mlog.Print(`start upgrading cli...`)
|
||||
var (
|
||||
downloadUrl = fmt.Sprintf(
|
||||
`https://github.com/gogf/gf/releases/latest/download/gf_%s_%s`,
|
||||
runtime.GOOS, runtime.GOARCH,
|
||||
)
|
||||
localSaveFilePath = gfile.SelfPath() + "~"
|
||||
)
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
downloadUrl += ".exe"
|
||||
}
|
||||
|
||||
mlog.Printf(`start downloading "%s" to "%s", it may take some time`, downloadUrl, localSaveFilePath)
|
||||
err = utils.HTTPDownloadFileWithPercent(downloadUrl, localSaveFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
mlog.Printf(`new version cli binary is successfully installed to "%s"`, gfile.SelfPath())
|
||||
mlog.Printf(`remove temporary buffer file "%s"`, localSaveFilePath)
|
||||
_ = gfile.Remove(localSaveFilePath)
|
||||
}()
|
||||
|
||||
// It fails if file not exist or its size is less than 1MB.
|
||||
if !gfile.Exists(localSaveFilePath) || gfile.Size(localSaveFilePath) < 1024*1024 {
|
||||
mlog.Fatalf(`download "%s" to "%s" failed`, downloadUrl, localSaveFilePath)
|
||||
}
|
||||
|
||||
newFile, err := gfile.Open(localSaveFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// selfupdate
|
||||
err = selfupdate.Apply(newFile, selfupdate.Options{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c cUp) doAutoFixing(ctx context.Context) (err error) {
|
||||
mlog.Print(`start auto fixing...`)
|
||||
err = cFix{}.doFix()
|
||||
func (c cUp) doAutoFixing(ctx context.Context, dirPath string, version string) (err error) {
|
||||
mlog.Printf(`auto fixing directory path "%s" from version "%s" ...`, dirPath, version)
|
||||
command := fmt.Sprintf(`gf fix -p %s`, dirPath)
|
||||
_ = gproc.ShellRun(ctx, command)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
@@ -64,6 +70,8 @@ func (c cVersion) getGFVersionOfCurrentProject() (string, error) {
|
||||
if gfile.Exists(goModPath) {
|
||||
lines := gstr.SplitAndTrim(gfile.GetContents(goModPath), "\n")
|
||||
for _, line := range lines {
|
||||
line = gstr.Trim(line)
|
||||
line = gstr.TrimLeftStr(line, "require ")
|
||||
line = gstr.Trim(line)
|
||||
// Version 1.
|
||||
match, err := gregex.MatchString(`^github\.com/gogf/gf\s+(.+)$`, line)
|
||||
|
||||
233
server/internal/library/hggen/internal/cmd/genctrl/genctrl.go
Normal file
233
server/internal/library/hggen/internal/cmd/genctrl/genctrl.go
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
|
||||
const (
|
||||
CGenCtrlConfig = `gfcli.gen.ctrl`
|
||||
CGenCtrlUsage = `gf gen ctrl [OPTION]`
|
||||
CGenCtrlBrief = `parse api definitions to generate controller/sdk go files`
|
||||
CGenCtrlEg = `
|
||||
gf gen ctrl
|
||||
`
|
||||
CGenCtrlBriefSrcFolder = `source folder path to be parsed. default: api`
|
||||
CGenCtrlBriefDstFolder = `destination folder path storing automatically generated go files. default: internal/controller`
|
||||
CGenCtrlBriefWatchFile = `used in file watcher, it re-generates go files only if given file is under srcFolder`
|
||||
CGenCtrlBriefSdkPath = `also generate SDK go files for api definitions to specified directory`
|
||||
CGenCtrlBriefSdkStdVersion = `use standard version prefix for generated sdk request path`
|
||||
CGenCtrlBriefSdkNoV1 = `do not add version suffix for interface module name if version is v1`
|
||||
CGenCtrlBriefClear = `auto delete generated and unimplemented controller go files if api definitions are missing`
|
||||
)
|
||||
|
||||
const (
|
||||
PatternApiDefinition = `type\s+(\w+)Req\s+struct\s+{`
|
||||
PatternCtrlDefinition = `func\s+\(.+?\)\s+\w+\(.+?\*(\w+)\.(\w+)Req\)\s+\(.+?\*(\w+)\.(\w+)Res,\s+\w+\s+error\)\s+{`
|
||||
)
|
||||
|
||||
const (
|
||||
genCtrlFileLockSeconds = 10
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`CGenCtrlConfig`: CGenCtrlConfig,
|
||||
`CGenCtrlUsage`: CGenCtrlUsage,
|
||||
`CGenCtrlBrief`: CGenCtrlBrief,
|
||||
`CGenCtrlEg`: CGenCtrlEg,
|
||||
`CGenCtrlBriefSrcFolder`: CGenCtrlBriefSrcFolder,
|
||||
`CGenCtrlBriefDstFolder`: CGenCtrlBriefDstFolder,
|
||||
`CGenCtrlBriefWatchFile`: CGenCtrlBriefWatchFile,
|
||||
`CGenCtrlBriefSdkPath`: CGenCtrlBriefSdkPath,
|
||||
`CGenCtrlBriefSdkStdVersion`: CGenCtrlBriefSdkStdVersion,
|
||||
`CGenCtrlBriefSdkNoV1`: CGenCtrlBriefSdkNoV1,
|
||||
`CGenCtrlBriefClear`: CGenCtrlBriefClear,
|
||||
})
|
||||
}
|
||||
|
||||
type (
|
||||
CGenCtrl struct{}
|
||||
CGenCtrlInput struct {
|
||||
g.Meta `name:"ctrl" config:"{CGenCtrlConfig}" usage:"{CGenCtrlUsage}" brief:"{CGenCtrlBrief}" eg:"{CGenCtrlEg}"`
|
||||
SrcFolder string `short:"s" name:"srcFolder" brief:"{CGenCtrlBriefSrcFolder}" d:"api"`
|
||||
DstFolder string `short:"d" name:"dstFolder" brief:"{CGenCtrlBriefDstFolder}" d:"internal/controller"`
|
||||
WatchFile string `short:"w" name:"watchFile" brief:"{CGenCtrlBriefWatchFile}"`
|
||||
SdkPath string `short:"k" name:"sdkPath" brief:"{CGenCtrlBriefSdkPath}"`
|
||||
SdkStdVersion bool `short:"v" name:"sdkStdVersion" brief:"{CGenCtrlBriefSdkStdVersion}" orphan:"true"`
|
||||
SdkNoV1 bool `short:"n" name:"sdkNoV1" brief:"{CGenCtrlBriefSdkNoV1}" orphan:"true"`
|
||||
Clear bool `short:"c" name:"clear" brief:"{CGenCtrlBriefClear}" orphan:"true"`
|
||||
}
|
||||
CGenCtrlOutput struct{}
|
||||
)
|
||||
|
||||
func (c CGenCtrl) Ctrl(ctx context.Context, in CGenCtrlInput) (out *CGenCtrlOutput, err error) {
|
||||
if in.WatchFile != "" {
|
||||
err = c.generateByWatchFile(
|
||||
in.WatchFile, in.SdkPath, in.SdkStdVersion, in.SdkNoV1, in.Clear,
|
||||
)
|
||||
mlog.Print(`done!`)
|
||||
return
|
||||
}
|
||||
|
||||
if !gfile.Exists(in.SrcFolder) {
|
||||
mlog.Fatalf(`source folder path "%s" does not exist`, in.SrcFolder)
|
||||
}
|
||||
// retrieve all api modules.
|
||||
apiModuleFolderPaths, err := gfile.ScanDir(in.SrcFolder, "*", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, apiModuleFolderPath := range apiModuleFolderPaths {
|
||||
if !gfile.IsDir(apiModuleFolderPath) {
|
||||
continue
|
||||
}
|
||||
// generate go files by api module.
|
||||
var (
|
||||
module = gfile.Basename(apiModuleFolderPath)
|
||||
dstModuleFolderPath = gfile.Join(in.DstFolder, module)
|
||||
)
|
||||
err = c.generateByModule(
|
||||
apiModuleFolderPath, dstModuleFolderPath, in.SdkPath,
|
||||
in.SdkStdVersion, in.SdkNoV1, in.Clear,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
mlog.Print(`done!`)
|
||||
return
|
||||
}
|
||||
|
||||
func (c CGenCtrl) generateByWatchFile(watchFile, sdkPath string, sdkStdVersion, sdkNoV1, clear bool) (err error) {
|
||||
// File lock to avoid multiple processes.
|
||||
var (
|
||||
flockFilePath = gfile.Temp("gf.cli.gen.service.lock")
|
||||
flockContent = gfile.GetContents(flockFilePath)
|
||||
)
|
||||
if flockContent != "" {
|
||||
if gtime.Timestamp()-gconv.Int64(flockContent) < genCtrlFileLockSeconds {
|
||||
// If another generating process is running, it just exits.
|
||||
mlog.Debug(`another "gen service" process is running, exit`)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer gfile.Remove(flockFilePath)
|
||||
_ = gfile.PutContents(flockFilePath, gtime.TimestampStr())
|
||||
|
||||
// check this updated file is an api file.
|
||||
// watch file should be in standard goframe project structure.
|
||||
var (
|
||||
apiVersionPath = gfile.Dir(watchFile)
|
||||
apiModuleFolderPath = gfile.Dir(apiVersionPath)
|
||||
shouldBeNameOfAPi = gfile.Basename(gfile.Dir(apiModuleFolderPath))
|
||||
)
|
||||
if shouldBeNameOfAPi != "api" {
|
||||
return nil
|
||||
}
|
||||
// watch file should have api definitions.
|
||||
if gfile.Exists(watchFile) {
|
||||
if !gregex.IsMatchString(PatternApiDefinition, gfile.GetContents(watchFile)) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
projectRootPath = gfile.Dir(gfile.Dir(apiModuleFolderPath))
|
||||
module = gfile.Basename(apiModuleFolderPath)
|
||||
dstModuleFolderPath = gfile.Join(projectRootPath, "internal", "controller", module)
|
||||
)
|
||||
return c.generateByModule(
|
||||
apiModuleFolderPath, dstModuleFolderPath, sdkPath, sdkStdVersion, sdkNoV1, clear,
|
||||
)
|
||||
}
|
||||
|
||||
// parseApiModule parses certain api and generate associated go files by certain module, not all api modules.
|
||||
func (c CGenCtrl) generateByModule(
|
||||
apiModuleFolderPath, dstModuleFolderPath, sdkPath string,
|
||||
sdkStdVersion, sdkNoV1, clear bool,
|
||||
) (err error) {
|
||||
// parse src and dst folder go files.
|
||||
apiItemsInSrc, err := c.getApiItemsInSrc(apiModuleFolderPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apiItemsInDst, err := c.getApiItemsInDst(dstModuleFolderPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// generate api interface go files.
|
||||
if err = newApiInterfaceGenerator().Generate(apiModuleFolderPath, apiItemsInSrc); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// generate controller go files.
|
||||
// api filtering for already implemented api controllers.
|
||||
var (
|
||||
alreadyImplementedCtrlSet = gset.NewStrSet()
|
||||
toBeImplementedApiItems = make([]apiItem, 0)
|
||||
)
|
||||
for _, item := range apiItemsInDst {
|
||||
alreadyImplementedCtrlSet.Add(item.String())
|
||||
}
|
||||
for _, item := range apiItemsInSrc {
|
||||
if alreadyImplementedCtrlSet.Contains(item.String()) {
|
||||
continue
|
||||
}
|
||||
toBeImplementedApiItems = append(toBeImplementedApiItems, item)
|
||||
}
|
||||
if len(toBeImplementedApiItems) > 0 {
|
||||
err = newControllerGenerator().Generate(dstModuleFolderPath, toBeImplementedApiItems)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// delete unimplemented controllers if api definitions are missing.
|
||||
if clear {
|
||||
var (
|
||||
apiDefinitionSet = gset.NewStrSet()
|
||||
extraApiItemsInCtrl = make([]apiItem, 0)
|
||||
)
|
||||
for _, item := range apiItemsInSrc {
|
||||
apiDefinitionSet.Add(item.String())
|
||||
}
|
||||
for _, item := range apiItemsInDst {
|
||||
if apiDefinitionSet.Contains(item.String()) {
|
||||
continue
|
||||
}
|
||||
extraApiItemsInCtrl = append(extraApiItemsInCtrl, item)
|
||||
}
|
||||
if len(extraApiItemsInCtrl) > 0 {
|
||||
err = newControllerClearer().Clear(dstModuleFolderPath, extraApiItemsInCtrl)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate sdk go files.
|
||||
if sdkPath != "" {
|
||||
if err = newApiSdkGenerator().Generate(apiItemsInSrc, sdkPath, sdkStdVersion, sdkNoV1); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import "github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
type apiItem struct {
|
||||
Import string `eg:"demo.com/api/user/v1"`
|
||||
Module string `eg:"user"`
|
||||
Version string `eg:"v1"`
|
||||
MethodName string `eg:"GetList"`
|
||||
}
|
||||
|
||||
func (a apiItem) String() string {
|
||||
return gstr.Join([]string{
|
||||
a.Import, a.Module, a.Version, a.MethodName,
|
||||
}, ",")
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"hotgo/internal/library/hggen/internal/utility/utils"
|
||||
)
|
||||
|
||||
func (c CGenCtrl) getApiItemsInSrc(apiModuleFolderPath string) (items []apiItem, err error) {
|
||||
var (
|
||||
fileContent string
|
||||
importPath string
|
||||
)
|
||||
// The second level folders: versions.
|
||||
apiVersionFolderPaths, err := gfile.ScanDir(apiModuleFolderPath, "*", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, apiVersionFolderPath := range apiVersionFolderPaths {
|
||||
if !gfile.IsDir(apiVersionFolderPath) {
|
||||
continue
|
||||
}
|
||||
// The second level folders: versions.
|
||||
apiFileFolderPaths, err := gfile.ScanDir(apiVersionFolderPath, "*.go", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
importPath = utils.GetImportPath(apiVersionFolderPath)
|
||||
for _, apiFileFolderPath := range apiFileFolderPaths {
|
||||
if gfile.IsDir(apiFileFolderPath) {
|
||||
continue
|
||||
}
|
||||
fileContent = gfile.GetContents(apiFileFolderPath)
|
||||
matches, err := gregex.MatchAllString(PatternApiDefinition, fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, match := range matches {
|
||||
item := apiItem{
|
||||
Import: gstr.Trim(importPath, `"`),
|
||||
Module: gfile.Basename(apiModuleFolderPath),
|
||||
Version: gfile.Basename(apiVersionFolderPath),
|
||||
MethodName: match[1],
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c CGenCtrl) getApiItemsInDst(dstFolder string) (items []apiItem, err error) {
|
||||
if !gfile.Exists(dstFolder) {
|
||||
return nil, nil
|
||||
}
|
||||
type importItem struct {
|
||||
Path string
|
||||
Alias string
|
||||
}
|
||||
var fileContent string
|
||||
filePaths, err := gfile.ScanDir(dstFolder, "*.go", true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, filePath := range filePaths {
|
||||
fileContent = gfile.GetContents(filePath)
|
||||
match, err := gregex.MatchString(`import\s+\(([\s\S]+?)\)`, fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(match) < 2 {
|
||||
continue
|
||||
}
|
||||
var (
|
||||
array []string
|
||||
importItems []importItem
|
||||
importLines = gstr.SplitAndTrim(match[1], "\n")
|
||||
module = gfile.Basename(gfile.Dir(filePath))
|
||||
)
|
||||
// retrieve all imports.
|
||||
for _, importLine := range importLines {
|
||||
array = gstr.SplitAndTrim(importLine, " ")
|
||||
if len(array) == 2 {
|
||||
importItems = append(importItems, importItem{
|
||||
Path: gstr.Trim(array[1], `"`),
|
||||
Alias: array[0],
|
||||
})
|
||||
} else {
|
||||
importItems = append(importItems, importItem{
|
||||
Path: gstr.Trim(array[0], `"`),
|
||||
})
|
||||
}
|
||||
}
|
||||
// retrieve all api usages.
|
||||
matches, err := gregex.MatchAllString(PatternCtrlDefinition, fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, match = range matches {
|
||||
// try to find the import path of the api.
|
||||
var (
|
||||
importPath string
|
||||
version = match[1]
|
||||
methodName = match[2] // not the function name, but the method name in api definition.
|
||||
)
|
||||
for _, item := range importItems {
|
||||
if item.Alias != "" {
|
||||
if item.Alias == version {
|
||||
importPath = item.Path
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if gfile.Basename(item.Path) == version {
|
||||
importPath = item.Path
|
||||
break
|
||||
}
|
||||
}
|
||||
item := apiItem{
|
||||
Import: gstr.Trim(importPath, `"`),
|
||||
Module: module,
|
||||
Version: gfile.Basename(importPath),
|
||||
MethodName: methodName,
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"hotgo/internal/library/hggen/internal/consts"
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
|
||||
type controllerGenerator struct{}
|
||||
|
||||
func newControllerGenerator() *controllerGenerator {
|
||||
return &controllerGenerator{}
|
||||
}
|
||||
|
||||
func (c *controllerGenerator) Generate(dstModuleFolderPath string, apiModuleApiItems []apiItem) (err error) {
|
||||
var (
|
||||
doneApiItemSet = gset.NewStrSet()
|
||||
)
|
||||
for _, item := range apiModuleApiItems {
|
||||
if doneApiItemSet.Contains(item.String()) {
|
||||
continue
|
||||
}
|
||||
// retrieve all api items of the same module.
|
||||
subItems := c.getSubItemsByModuleAndVersion(apiModuleApiItems, item.Module, item.Version)
|
||||
if err = c.doGenerateCtrlNewByModuleAndVersion(
|
||||
dstModuleFolderPath, item.Module, item.Version, gfile.Dir(item.Import),
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
for _, subItem := range subItems {
|
||||
if err = c.doGenerateCtrlItem(dstModuleFolderPath, subItem); err != nil {
|
||||
return
|
||||
}
|
||||
doneApiItemSet.Add(subItem.String())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *controllerGenerator) getSubItemsByModuleAndVersion(items []apiItem, module, version string) (subItems []apiItem) {
|
||||
for _, item := range items {
|
||||
if item.Module == module && item.Version == version {
|
||||
subItems = append(subItems, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *controllerGenerator) doGenerateCtrlNewByModuleAndVersion(
|
||||
dstModuleFolderPath, module, version, importPath string,
|
||||
) (err error) {
|
||||
var (
|
||||
moduleFilePath = gfile.Join(dstModuleFolderPath, module+".go")
|
||||
moduleFilePathNew = gfile.Join(dstModuleFolderPath, module+"_new.go")
|
||||
ctrlName = fmt.Sprintf(`Controller%s`, gstr.UcFirst(version))
|
||||
interfaceName = fmt.Sprintf(`%s.I%s%s`, module, gstr.CaseCamel(module), gstr.UcFirst(version))
|
||||
newFuncName = fmt.Sprintf(`New%s`, gstr.UcFirst(version))
|
||||
newFuncNameDefinition = fmt.Sprintf(`func %s()`, newFuncName)
|
||||
alreadyCreated bool
|
||||
)
|
||||
// replace "\" to "/", fix import error
|
||||
importPath = gstr.Replace(importPath, "\\", "/", -1)
|
||||
if !gfile.Exists(moduleFilePath) {
|
||||
content := gstr.ReplaceByMap(consts.TemplateGenCtrlControllerEmpty, g.MapStrStr{
|
||||
"{Module}": module,
|
||||
})
|
||||
if err = gfile.PutContents(moduleFilePath, gstr.TrimLeft(content)); err != nil {
|
||||
return err
|
||||
}
|
||||
mlog.Printf(`generated: %s`, moduleFilePath)
|
||||
}
|
||||
if !gfile.Exists(moduleFilePathNew) {
|
||||
content := gstr.ReplaceByMap(consts.TemplateGenCtrlControllerNewEmpty, g.MapStrStr{
|
||||
"{Module}": module,
|
||||
"{ImportPath}": fmt.Sprintf(`"%s"`, importPath),
|
||||
})
|
||||
if err = gfile.PutContents(moduleFilePathNew, gstr.TrimLeft(content)); err != nil {
|
||||
return err
|
||||
}
|
||||
mlog.Printf(`generated: %s`, moduleFilePathNew)
|
||||
}
|
||||
filePaths, err := gfile.ScanDir(dstModuleFolderPath, "*.go", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, filePath := range filePaths {
|
||||
if gstr.Contains(gfile.GetContents(filePath), newFuncNameDefinition) {
|
||||
alreadyCreated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !alreadyCreated {
|
||||
content := gstr.ReplaceByMap(consts.TemplateGenCtrlControllerNewFunc, g.MapStrStr{
|
||||
"{CtrlName}": ctrlName,
|
||||
"{NewFuncName}": newFuncName,
|
||||
"{InterfaceName}": interfaceName,
|
||||
})
|
||||
err = gfile.PutContentsAppend(moduleFilePathNew, gstr.TrimLeft(content))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *controllerGenerator) doGenerateCtrlItem(dstModuleFolderPath string, item apiItem) (err error) {
|
||||
var (
|
||||
methodNameSnake = gstr.CaseSnake(item.MethodName)
|
||||
ctrlName = fmt.Sprintf(`Controller%s`, gstr.UcFirst(item.Version))
|
||||
methodFilePath = gfile.Join(dstModuleFolderPath, fmt.Sprintf(
|
||||
`%s_%s_%s.go`, item.Module, item.Version, methodNameSnake,
|
||||
))
|
||||
)
|
||||
content := gstr.ReplaceByMap(consts.TemplateGenCtrlControllerMethodFunc, g.MapStrStr{
|
||||
"{Module}": item.Module,
|
||||
"{ImportPath}": item.Import,
|
||||
"{CtrlName}": ctrlName,
|
||||
"{Version}": item.Version,
|
||||
"{MethodName}": item.MethodName,
|
||||
})
|
||||
if err = gfile.PutContents(methodFilePath, gstr.TrimLeft(content)); err != nil {
|
||||
return err
|
||||
}
|
||||
mlog.Printf(`generated: %s`, methodFilePath)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
|
||||
type controllerClearer struct{}
|
||||
|
||||
func newControllerClearer() *controllerClearer {
|
||||
return &controllerClearer{}
|
||||
}
|
||||
|
||||
func (c *controllerClearer) Clear(dstModuleFolderPath string, extraApiItemsInCtrl []apiItem) (err error) {
|
||||
for _, item := range extraApiItemsInCtrl {
|
||||
if err = c.doClear(dstModuleFolderPath, item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *controllerClearer) doClear(dstModuleFolderPath string, item apiItem) (err error) {
|
||||
var (
|
||||
methodNameSnake = gstr.CaseSnake(item.MethodName)
|
||||
methodFilePath = gfile.Join(dstModuleFolderPath, fmt.Sprintf(
|
||||
`%s_%s_%s.go`, item.Module, item.Version, methodNameSnake,
|
||||
))
|
||||
fileContent = gstr.Trim(gfile.GetContents(methodFilePath))
|
||||
)
|
||||
match, err := gregex.MatchString(`.+?Req.+?Res.+?{([\s\S]+?)}`, fileContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(match) > 1 {
|
||||
implements := gstr.Trim(match[1])
|
||||
// One line.
|
||||
if !gstr.Contains(implements, "\n") && gstr.Contains(implements, `CodeNotImplemented`) {
|
||||
mlog.Printf(
|
||||
`remove unimplemented and of no api definitions controller file: %s`,
|
||||
methodFilePath,
|
||||
)
|
||||
err = gfile.Remove(methodFilePath)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"hotgo/internal/library/hggen/internal/consts"
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
|
||||
type apiInterfaceGenerator struct{}
|
||||
|
||||
func newApiInterfaceGenerator() *apiInterfaceGenerator {
|
||||
return &apiInterfaceGenerator{}
|
||||
}
|
||||
|
||||
func (c *apiInterfaceGenerator) Generate(apiModuleFolderPath string, apiModuleApiItems []apiItem) (err error) {
|
||||
if len(apiModuleApiItems) == 0 {
|
||||
return nil
|
||||
}
|
||||
var firstApiItem = apiModuleApiItems[0]
|
||||
if err = c.doGenerate(apiModuleFolderPath, firstApiItem.Module, apiModuleApiItems); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiInterfaceGenerator) doGenerate(apiModuleFolderPath string, module string, items []apiItem) (err error) {
|
||||
var (
|
||||
moduleFilePath = gfile.Join(apiModuleFolderPath, fmt.Sprintf(`%s.go`, module))
|
||||
importPathMap = gmap.NewListMap()
|
||||
importPaths []string
|
||||
)
|
||||
// all import paths.
|
||||
importPathMap.Set("\t"+`"context"`, 1)
|
||||
importPathMap.Set("\t"+``, 1)
|
||||
for _, item := range items {
|
||||
importPathMap.Set(fmt.Sprintf("\t"+`"%s"`, item.Import), 1)
|
||||
}
|
||||
importPaths = gconv.Strings(importPathMap.Keys())
|
||||
// interface definitions.
|
||||
var (
|
||||
doneApiItemSet = gset.NewStrSet()
|
||||
interfaceDefinition string
|
||||
interfaceContent = gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlApiInterface, g.MapStrStr{
|
||||
"{Module}": module,
|
||||
"{ImportPaths}": gstr.Join(importPaths, "\n"),
|
||||
}))
|
||||
)
|
||||
for _, item := range items {
|
||||
if doneApiItemSet.Contains(item.String()) {
|
||||
continue
|
||||
}
|
||||
// retrieve all api items of the same module.
|
||||
subItems := c.getSubItemsByModuleAndVersion(items, item.Module, item.Version)
|
||||
var (
|
||||
method string
|
||||
methods = make([]string, 0)
|
||||
interfaceName = fmt.Sprintf(`I%s%s`, gstr.CaseCamel(item.Module), gstr.UcFirst(item.Version))
|
||||
)
|
||||
for _, subItem := range subItems {
|
||||
method = fmt.Sprintf(
|
||||
"\t%s(ctx context.Context, req *%s.%sReq) (res *%s.%sRes, err error)",
|
||||
subItem.MethodName, subItem.Version, subItem.MethodName, subItem.Version, subItem.MethodName,
|
||||
)
|
||||
methods = append(methods, method)
|
||||
doneApiItemSet.Add(subItem.String())
|
||||
}
|
||||
interfaceDefinition += fmt.Sprintf("type %s interface {", interfaceName)
|
||||
interfaceDefinition += "\n"
|
||||
interfaceDefinition += gstr.Join(methods, "\n")
|
||||
interfaceDefinition += "\n"
|
||||
interfaceDefinition += fmt.Sprintf("}")
|
||||
interfaceDefinition += "\n\n"
|
||||
}
|
||||
interfaceContent = gstr.TrimLeft(gstr.ReplaceByMap(interfaceContent, g.MapStrStr{
|
||||
"{Interfaces}": interfaceDefinition,
|
||||
}))
|
||||
err = gfile.PutContents(moduleFilePath, interfaceContent)
|
||||
mlog.Printf(`generated: %s`, moduleFilePath)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiInterfaceGenerator) getSubItemsByModule(items []apiItem, module string) (subItems []apiItem) {
|
||||
for _, item := range items {
|
||||
if item.Module == module {
|
||||
subItems = append(subItems, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiInterfaceGenerator) getSubItemsByModuleAndVersion(items []apiItem, module, version string) (subItems []apiItem) {
|
||||
for _, item := range items {
|
||||
if item.Module == module && item.Version == version {
|
||||
subItems = append(subItems, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genctrl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"hotgo/internal/library/hggen/internal/consts"
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
|
||||
type apiSdkGenerator struct{}
|
||||
|
||||
func newApiSdkGenerator() *apiSdkGenerator {
|
||||
return &apiSdkGenerator{}
|
||||
}
|
||||
|
||||
func (c *apiSdkGenerator) Generate(apiModuleApiItems []apiItem, sdkFolderPath string, sdkStdVersion, sdkNoV1 bool) (err error) {
|
||||
if err = c.doGenerateSdkPkgFile(sdkFolderPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var doneApiItemSet = gset.NewStrSet()
|
||||
for _, item := range apiModuleApiItems {
|
||||
if doneApiItemSet.Contains(item.String()) {
|
||||
continue
|
||||
}
|
||||
// retrieve all api items of the same module.
|
||||
subItems := c.getSubItemsByModuleAndVersion(apiModuleApiItems, item.Module, item.Version)
|
||||
if err = c.doGenerateSdkIClient(sdkFolderPath, item.Import, item.Module, item.Version, sdkNoV1); err != nil {
|
||||
return
|
||||
}
|
||||
if err = c.doGenerateSdkImplementer(
|
||||
subItems, sdkFolderPath, item.Import, item.Module, item.Version, sdkStdVersion, sdkNoV1,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
for _, subItem := range subItems {
|
||||
doneApiItemSet.Add(subItem.String())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiSdkGenerator) doGenerateSdkPkgFile(sdkFolderPath string) (err error) {
|
||||
var (
|
||||
pkgName = gfile.Basename(sdkFolderPath)
|
||||
pkgFilePath = gfile.Join(sdkFolderPath, fmt.Sprintf(`%s.go`, pkgName))
|
||||
fileContent string
|
||||
)
|
||||
if gfile.Exists(pkgFilePath) {
|
||||
return nil
|
||||
}
|
||||
fileContent = gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlSdkPkgNew, g.MapStrStr{
|
||||
"{PkgName}": pkgName,
|
||||
}))
|
||||
err = gfile.PutContents(pkgFilePath, fileContent)
|
||||
mlog.Printf(`generated: %s`, pkgFilePath)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiSdkGenerator) doGenerateSdkIClient(
|
||||
sdkFolderPath, versionImportPath, module, version string, sdkNoV1 bool,
|
||||
) (err error) {
|
||||
var (
|
||||
fileContent string
|
||||
isDirty bool
|
||||
isExist bool
|
||||
pkgName = gfile.Basename(sdkFolderPath)
|
||||
funcName = gstr.CaseCamel(module) + gstr.UcFirst(version)
|
||||
interfaceName = fmt.Sprintf(`I%s`, funcName)
|
||||
moduleImportPath = fmt.Sprintf(`"%s"`, gfile.Dir(versionImportPath))
|
||||
iClientFilePath = gfile.Join(sdkFolderPath, fmt.Sprintf(`%s.iclient.go`, pkgName))
|
||||
interfaceFuncDefinition = fmt.Sprintf(
|
||||
`%s() %s.%s`,
|
||||
gstr.CaseCamel(module)+gstr.UcFirst(version), module, interfaceName,
|
||||
)
|
||||
)
|
||||
if sdkNoV1 && version == "v1" {
|
||||
interfaceFuncDefinition = fmt.Sprintf(
|
||||
`%s() %s.%s`,
|
||||
gstr.CaseCamel(module), module, interfaceName,
|
||||
)
|
||||
}
|
||||
if isExist = gfile.Exists(iClientFilePath); isExist {
|
||||
fileContent = gfile.GetContents(iClientFilePath)
|
||||
} else {
|
||||
fileContent = gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlSdkIClient, g.MapStrStr{
|
||||
"{PkgName}": pkgName,
|
||||
}))
|
||||
}
|
||||
|
||||
// append the import path to current import paths.
|
||||
if !gstr.Contains(fileContent, moduleImportPath) {
|
||||
isDirty = true
|
||||
fileContent, err = gregex.ReplaceString(
|
||||
`(import \([\s\S]*?)\)`,
|
||||
fmt.Sprintf("$1\t%s\n)", moduleImportPath),
|
||||
fileContent,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// append the function definition to interface definition.
|
||||
if !gstr.Contains(fileContent, interfaceFuncDefinition) {
|
||||
isDirty = true
|
||||
fileContent, err = gregex.ReplaceString(
|
||||
`(type iClient interface {[\s\S]*?)}`,
|
||||
fmt.Sprintf("$1\t%s\n}", interfaceFuncDefinition),
|
||||
fileContent,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if isDirty {
|
||||
err = gfile.PutContents(iClientFilePath, fileContent)
|
||||
if isExist {
|
||||
mlog.Printf(`updated: %s`, iClientFilePath)
|
||||
} else {
|
||||
mlog.Printf(`generated: %s`, iClientFilePath)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiSdkGenerator) doGenerateSdkImplementer(
|
||||
items []apiItem, sdkFolderPath, versionImportPath, module, version string, sdkStdVersion, sdkNoV1 bool,
|
||||
) (err error) {
|
||||
var (
|
||||
pkgName = gfile.Basename(sdkFolderPath)
|
||||
moduleNameCamel = gstr.CaseCamel(module)
|
||||
moduleNameSnake = gstr.CaseSnake(module)
|
||||
moduleImportPath = gfile.Dir(versionImportPath)
|
||||
versionPrefix = ""
|
||||
implementerName = moduleNameCamel + gstr.UcFirst(version)
|
||||
implementerFilePath = gfile.Join(sdkFolderPath, fmt.Sprintf(
|
||||
`%s_%s_%s.go`, pkgName, moduleNameSnake, version,
|
||||
))
|
||||
)
|
||||
if sdkNoV1 && version == "v1" {
|
||||
implementerName = moduleNameCamel
|
||||
}
|
||||
// implementer file template.
|
||||
var importPaths = make([]string, 0)
|
||||
importPaths = append(importPaths, fmt.Sprintf("\t\"%s\"", moduleImportPath))
|
||||
importPaths = append(importPaths, fmt.Sprintf("\t\"%s\"", versionImportPath))
|
||||
implementerFileContent := gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlSdkImplementer, g.MapStrStr{
|
||||
"{PkgName}": pkgName,
|
||||
"{ImportPaths}": gstr.Join(importPaths, "\n"),
|
||||
"{ImplementerName}": implementerName,
|
||||
}))
|
||||
// implementer new function definition.
|
||||
if sdkStdVersion {
|
||||
versionPrefix = fmt.Sprintf(`/api/%s`, version)
|
||||
}
|
||||
implementerFileContent += gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlSdkImplementerNew, g.MapStrStr{
|
||||
"{Module}": module,
|
||||
"{VersionPrefix}": versionPrefix,
|
||||
"{ImplementerName}": implementerName,
|
||||
}))
|
||||
// implementer functions definitions.
|
||||
for _, item := range items {
|
||||
implementerFileContent += gstr.TrimLeft(gstr.ReplaceByMap(consts.TemplateGenCtrlSdkImplementerFunc, g.MapStrStr{
|
||||
"{Version}": item.Version,
|
||||
"{MethodName}": item.MethodName,
|
||||
"{ImplementerName}": implementerName,
|
||||
}))
|
||||
implementerFileContent += "\n"
|
||||
}
|
||||
err = gfile.PutContents(implementerFilePath, implementerFileContent)
|
||||
mlog.Printf(`generated: %s`, implementerFilePath)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *apiSdkGenerator) getSubItemsByModuleAndVersion(items []apiItem, module, version string) (subItems []apiItem) {
|
||||
for _, item := range items {
|
||||
if item.Module == module && item.Version == version {
|
||||
subItems = append(subItems, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,18 +1,26 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"golang.org/x/mod/modfile"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"hotgo/internal/library/hggen/internal/utility/utils"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
@@ -43,6 +51,12 @@ CONFIGURATION SUPPORT
|
||||
path: "./my-app"
|
||||
prefix: "primary_"
|
||||
tables: "user, userDetail"
|
||||
typeMapping:
|
||||
decimal:
|
||||
type: decimal.Decimal
|
||||
import: github.com/shopspring/decimal
|
||||
numeric:
|
||||
type: string
|
||||
`
|
||||
CGenDaoBriefPath = `directory path for generated files`
|
||||
CGenDaoBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
@@ -64,6 +78,7 @@ CONFIGURATION SUPPORT
|
||||
CGenDaoBriefNoJsonTag = `no json tag will be added for each field`
|
||||
CGenDaoBriefNoModelComment = `no model comment will be added for each field`
|
||||
CGenDaoBriefClear = `delete all generated go files that do not exist in database`
|
||||
CGenDaoBriefTypeMapping = `custom local type mapping for generated struct attributes relevant to fields of table`
|
||||
CGenDaoBriefGroup = `
|
||||
specifying the configuration group name of database for generated ORM instance,
|
||||
it's not necessary and the default value is "default"
|
||||
@@ -99,7 +114,21 @@ generated json tag case for model struct, cases are as follows:
|
||||
)
|
||||
|
||||
var (
|
||||
createdAt = gtime.Now()
|
||||
createdAt = gtime.Now()
|
||||
defaultTypeMapping = map[string]TypeMapping{
|
||||
"decimal": {
|
||||
Type: "float64",
|
||||
},
|
||||
"money": {
|
||||
Type: "float64",
|
||||
},
|
||||
"numeric": {
|
||||
Type: "float64",
|
||||
},
|
||||
"smallmoney": {
|
||||
Type: "float64",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -129,6 +158,7 @@ func init() {
|
||||
`CGenDaoBriefNoJsonTag`: CGenDaoBriefNoJsonTag,
|
||||
`CGenDaoBriefNoModelComment`: CGenDaoBriefNoModelComment,
|
||||
`CGenDaoBriefClear`: CGenDaoBriefClear,
|
||||
`CGenDaoBriefTypeMapping`: CGenDaoBriefTypeMapping,
|
||||
`CGenDaoBriefGroup`: CGenDaoBriefGroup,
|
||||
`CGenDaoBriefJsonCase`: CGenDaoBriefJsonCase,
|
||||
`CGenDaoBriefTplDaoIndexPath`: CGenDaoBriefTplDaoIndexPath,
|
||||
@@ -142,30 +172,31 @@ type (
|
||||
CGenDao struct{}
|
||||
CGenDaoInput struct {
|
||||
g.Meta `name:"dao" config:"{CGenDaoConfig}" usage:"{CGenDaoUsage}" brief:"{CGenDaoBrief}" eg:"{CGenDaoEg}" ad:"{CGenDaoAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{CGenDaoBriefPath}" d:"internal"`
|
||||
Link string `name:"link" short:"l" brief:"{CGenDaoBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{CGenDaoBriefTables}"`
|
||||
TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"`
|
||||
Group string `name:"group" short:"g" brief:"{CGenDaoBriefGroup}" d:"default"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{CGenDaoBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenDaoBriefRemovePrefix}"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenDaoBriefJsonCase}" d:"CamelLower"`
|
||||
ImportPrefix string `name:"importPrefix" short:"i" brief:"{CGenDaoBriefImportPrefix}"`
|
||||
DaoPath string `name:"daoPath" short:"d" brief:"{CGenDaoBriefDaoPath}" d:"dao"`
|
||||
DoPath string `name:"doPath" short:"o" brief:"{CGenDaoBriefDoPath}" d:"model/do"`
|
||||
EntityPath string `name:"entityPath" short:"e" brief:"{CGenDaoBriefEntityPath}" d:"model/entity"`
|
||||
TplDaoIndexPath string `name:"tplDaoIndexPath" short:"t1" brief:"{CGenDaoBriefTplDaoIndexPath}"`
|
||||
TplDaoInternalPath string `name:"tplDaoInternalPath" short:"t2" brief:"{CGenDaoBriefTplDaoInternalPath}"`
|
||||
TplDaoDoPath string `name:"tplDaoDoPath" short:"t3" brief:"{CGenDaoBriefTplDaoDoPathPath}"`
|
||||
TplDaoEntityPath string `name:"tplDaoEntityPath" short:"t4" brief:"{CGenDaoBriefTplDaoEntityPath}"`
|
||||
StdTime bool `name:"stdTime" short:"s" brief:"{CGenDaoBriefStdTime}" orphan:"true"`
|
||||
WithTime bool `name:"withTime" short:"w" brief:"{CGenDaoBriefWithTime}" orphan:"true"`
|
||||
GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{CGenDaoBriefGJsonSupport}" orphan:"true"`
|
||||
OverwriteDao bool `name:"overwriteDao" short:"v" brief:"{CGenDaoBriefOverwriteDao}" orphan:"true"`
|
||||
DescriptionTag bool `name:"descriptionTag" short:"c" brief:"{CGenDaoBriefDescriptionTag}" orphan:"true"`
|
||||
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{CGenDaoBriefNoJsonTag}" orphan:"true"`
|
||||
NoModelComment bool `name:"noModelComment" short:"m" brief:"{CGenDaoBriefNoModelComment}" orphan:"true"`
|
||||
Clear bool `name:"clear" short:"a" brief:"{CGenDaoBriefClear}" orphan:"true"`
|
||||
Path string `name:"path" short:"p" brief:"{CGenDaoBriefPath}" d:"internal"`
|
||||
Link string `name:"link" short:"l" brief:"{CGenDaoBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{CGenDaoBriefTables}"`
|
||||
TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"`
|
||||
Group string `name:"group" short:"g" brief:"{CGenDaoBriefGroup}" d:"default"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{CGenDaoBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenDaoBriefRemovePrefix}"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenDaoBriefJsonCase}" d:"CamelLower"`
|
||||
ImportPrefix string `name:"importPrefix" short:"i" brief:"{CGenDaoBriefImportPrefix}"`
|
||||
DaoPath string `name:"daoPath" short:"d" brief:"{CGenDaoBriefDaoPath}" d:"dao"`
|
||||
DoPath string `name:"doPath" short:"o" brief:"{CGenDaoBriefDoPath}" d:"model/do"`
|
||||
EntityPath string `name:"entityPath" short:"e" brief:"{CGenDaoBriefEntityPath}" d:"model/entity"`
|
||||
TplDaoIndexPath string `name:"tplDaoIndexPath" short:"t1" brief:"{CGenDaoBriefTplDaoIndexPath}"`
|
||||
TplDaoInternalPath string `name:"tplDaoInternalPath" short:"t2" brief:"{CGenDaoBriefTplDaoInternalPath}"`
|
||||
TplDaoDoPath string `name:"tplDaoDoPath" short:"t3" brief:"{CGenDaoBriefTplDaoDoPathPath}"`
|
||||
TplDaoEntityPath string `name:"tplDaoEntityPath" short:"t4" brief:"{CGenDaoBriefTplDaoEntityPath}"`
|
||||
StdTime bool `name:"stdTime" short:"s" brief:"{CGenDaoBriefStdTime}" orphan:"true"`
|
||||
WithTime bool `name:"withTime" short:"w" brief:"{CGenDaoBriefWithTime}" orphan:"true"`
|
||||
GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{CGenDaoBriefGJsonSupport}" orphan:"true"`
|
||||
OverwriteDao bool `name:"overwriteDao" short:"v" brief:"{CGenDaoBriefOverwriteDao}" orphan:"true"`
|
||||
DescriptionTag bool `name:"descriptionTag" short:"c" brief:"{CGenDaoBriefDescriptionTag}" orphan:"true"`
|
||||
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{CGenDaoBriefNoJsonTag}" orphan:"true"`
|
||||
NoModelComment bool `name:"noModelComment" short:"m" brief:"{CGenDaoBriefNoModelComment}" orphan:"true"`
|
||||
Clear bool `name:"clear" short:"a" brief:"{CGenDaoBriefClear}" orphan:"true"`
|
||||
TypeMapping map[string]TypeMapping `name:"typeMapping" short:"y" brief:"{CGenDaoBriefTypeMapping}" orphan:"true"`
|
||||
}
|
||||
CGenDaoOutput struct{}
|
||||
|
||||
@@ -174,7 +205,11 @@ type (
|
||||
DB gdb.DB
|
||||
TableNames []string
|
||||
NewTableNames []string
|
||||
ModName string // Module name of current golang project, which is used for import purpose.
|
||||
}
|
||||
|
||||
TypeMapping struct {
|
||||
Type string `brief:"custom attribute type name"`
|
||||
Import string `brief:"custom import for this type"`
|
||||
}
|
||||
)
|
||||
|
||||
@@ -195,16 +230,11 @@ func (c CGenDao) Dao(ctx context.Context, in CGenDaoInput) (out *CGenDaoOutput,
|
||||
return
|
||||
}
|
||||
|
||||
func DoGenDaoForArray(ctx context.Context, in CGenDaoInput) {
|
||||
doGenDaoForArray(ctx, -1, in)
|
||||
}
|
||||
|
||||
// doGenDaoForArray implements the "gen dao" command for configuration array.
|
||||
func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
|
||||
var (
|
||||
err error
|
||||
db gdb.DB
|
||||
modName string // Go module name, eg: github.com/gogf/gf.
|
||||
err error
|
||||
db gdb.DB
|
||||
)
|
||||
if index >= 0 {
|
||||
err = g.Cfg().MustGet(
|
||||
@@ -219,20 +249,6 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
|
||||
mlog.Fatalf(`path "%s" does not exist`, in.Path)
|
||||
}
|
||||
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
|
||||
if in.ImportPrefix == "" {
|
||||
if !gfile.Exists("go.mod") {
|
||||
mlog.Fatal("go.mod does not exist in current working directory")
|
||||
}
|
||||
var (
|
||||
goModContent = gfile.GetContents("go.mod")
|
||||
match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
modName = gstr.Trim(match[1])
|
||||
} else {
|
||||
mlog.Fatal("module name does not found in go.mod")
|
||||
}
|
||||
}
|
||||
|
||||
// It uses user passed database configuration.
|
||||
if in.Link != "" {
|
||||
@@ -268,6 +284,17 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
|
||||
tableNames = array.Slice()
|
||||
}
|
||||
|
||||
// merge default typeMapping to input typeMapping.
|
||||
if in.TypeMapping == nil {
|
||||
in.TypeMapping = defaultTypeMapping
|
||||
} else {
|
||||
for key, typeMapping := range defaultTypeMapping {
|
||||
if _, ok := in.TypeMapping[key]; !ok {
|
||||
in.TypeMapping[key] = typeMapping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generating dao & model go files one by one according to given table name.
|
||||
newTableNames := make([]string, len(tableNames))
|
||||
for i, tableName := range tableNames {
|
||||
@@ -278,13 +305,13 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
|
||||
newTableName = in.Prefix + newTableName
|
||||
newTableNames[i] = newTableName
|
||||
}
|
||||
|
||||
// Dao: index and internal.
|
||||
generateDao(ctx, CGenDaoInternalInput{
|
||||
CGenDaoInput: in,
|
||||
DB: db,
|
||||
TableNames: tableNames,
|
||||
NewTableNames: newTableNames,
|
||||
ModName: modName,
|
||||
})
|
||||
// Do.
|
||||
generateDo(ctx, CGenDaoInternalInput{
|
||||
@@ -292,7 +319,6 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
|
||||
DB: db,
|
||||
TableNames: tableNames,
|
||||
NewTableNames: newTableNames,
|
||||
ModName: modName,
|
||||
})
|
||||
// Entity.
|
||||
generateEntity(ctx, CGenDaoInternalInput{
|
||||
@@ -300,15 +326,11 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
|
||||
DB: db,
|
||||
TableNames: tableNames,
|
||||
NewTableNames: newTableNames,
|
||||
ModName: modName,
|
||||
})
|
||||
}
|
||||
|
||||
func getImportPartContent(source string, isDo bool) string {
|
||||
var (
|
||||
packageImportsArray = garray.NewStrArray()
|
||||
)
|
||||
|
||||
func getImportPartContent(ctx context.Context, source string, isDo bool, appendImports []string) string {
|
||||
var packageImportsArray = garray.NewStrArray()
|
||||
if isDo {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/frame/g"`)
|
||||
}
|
||||
@@ -325,6 +347,32 @@ func getImportPartContent(source string, isDo bool) string {
|
||||
packageImportsArray.Append(`"github.com/gogf/gf/v2/encoding/gjson"`)
|
||||
}
|
||||
|
||||
// Check and update imports in go.mod
|
||||
if appendImports != nil && len(appendImports) > 0 {
|
||||
goModPath := utils.GetModPath()
|
||||
if goModPath == "" {
|
||||
mlog.Fatal("go.mod not found in current project")
|
||||
}
|
||||
mod, err := modfile.Parse(goModPath, gfile.GetBytes(goModPath), nil)
|
||||
if err != nil {
|
||||
mlog.Fatalf("parse go.mod failed: %+v", err)
|
||||
}
|
||||
for _, appendImport := range appendImports {
|
||||
found := false
|
||||
for _, require := range mod.Require {
|
||||
if gstr.Contains(appendImport, require.Mod.Path) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
err = gproc.ShellRun(ctx, `go get `+appendImport)
|
||||
mlog.Fatalf(`%+v`, err)
|
||||
}
|
||||
packageImportsArray.Append(fmt.Sprintf(`"%s"`, appendImport))
|
||||
}
|
||||
}
|
||||
|
||||
// Generate and write content to golang file.
|
||||
packageImportsStr := ""
|
||||
if packageImportsArray.Len() > 0 {
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
@@ -6,12 +12,12 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/consts"
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
@@ -53,23 +59,13 @@ func generateDaoSingle(ctx context.Context, in generateDaoSingleInput) {
|
||||
mlog.Fatalf(`fetching tables fields failed for table "%s": %+v`, in.TableName, err)
|
||||
}
|
||||
var (
|
||||
dirRealPath = gfile.RealPath(in.Path)
|
||||
tableNameCamelCase = gstr.CaseCamel(in.NewTableName)
|
||||
tableNameCamelLowerCase = gstr.CaseCamelLower(in.NewTableName)
|
||||
tableNameSnakeCase = gstr.CaseSnake(in.NewTableName)
|
||||
importPrefix = in.ImportPrefix
|
||||
)
|
||||
if importPrefix == "" {
|
||||
if dirRealPath == "" {
|
||||
dirRealPath = in.Path
|
||||
importPrefix = dirRealPath
|
||||
importPrefix = gstr.Trim(dirRealPath, "./")
|
||||
} else {
|
||||
importPrefix = gstr.Replace(dirRealPath, gfile.Pwd(), "")
|
||||
}
|
||||
importPrefix = gstr.Replace(importPrefix, gfile.Separator, "/")
|
||||
importPrefix = gstr.Join(g.SliceStr{in.ModName, importPrefix, in.DaoPath}, "/")
|
||||
importPrefix, _ = gregex.ReplaceString(`\/{2,}`, `/`, gstr.Trim(importPrefix, "/"))
|
||||
importPrefix = utils.GetImportPath(gfile.Join(in.Path, in.DaoPath))
|
||||
} else {
|
||||
importPrefix = gstr.Join(g.SliceStr{importPrefix, in.DaoPath}, "/")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
@@ -30,9 +36,9 @@ func generateDo(ctx context.Context, in CGenDaoInternalInput) {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", tableName, err)
|
||||
}
|
||||
var (
|
||||
newTableName = in.NewTableNames[i]
|
||||
doFilePath = gfile.Join(dirPathDo, gstr.CaseSnake(newTableName)+".go")
|
||||
structDefinition = generateStructDefinition(ctx, generateStructDefinitionInput{
|
||||
newTableName = in.NewTableNames[i]
|
||||
doFilePath = gfile.Join(dirPathDo, gstr.CaseSnake(newTableName)+".go")
|
||||
structDefinition, _ = generateStructDefinition(ctx, generateStructDefinitionInput{
|
||||
CGenDaoInternalInput: in,
|
||||
TableName: tableName,
|
||||
StructName: gstr.CaseCamel(newTableName),
|
||||
@@ -53,6 +59,7 @@ func generateDo(ctx context.Context, in CGenDaoInternalInput) {
|
||||
},
|
||||
)
|
||||
modelContent := generateDoContent(
|
||||
ctx,
|
||||
in,
|
||||
tableName,
|
||||
gstr.CaseCamel(newTableName),
|
||||
@@ -68,15 +75,18 @@ func generateDo(ctx context.Context, in CGenDaoInternalInput) {
|
||||
}
|
||||
}
|
||||
|
||||
func generateDoContent(in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
|
||||
func generateDoContent(
|
||||
ctx context.Context, in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string,
|
||||
) string {
|
||||
doContent := gstr.ReplaceByMap(
|
||||
getTemplateFromPathOrDefault(in.TplDaoDoPath, consts.TemplateGenDaoDoContent),
|
||||
g.MapStrStr{
|
||||
tplVarTableName: tableName,
|
||||
tplVarPackageImports: getImportPartContent(structDefine, true),
|
||||
tplVarPackageImports: getImportPartContent(ctx, structDefine, true, nil),
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarStructDefine: structDefine,
|
||||
})
|
||||
},
|
||||
)
|
||||
doContent = replaceDefaultVar(in, doContent)
|
||||
return doContent
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
@@ -24,22 +30,27 @@ func generateEntity(ctx context.Context, in CGenDaoInternalInput) {
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", tableName, err)
|
||||
}
|
||||
|
||||
var (
|
||||
newTableName = in.NewTableNames[i]
|
||||
entityFilePath = gfile.Join(dirPathEntity, gstr.CaseSnake(newTableName)+".go")
|
||||
entityContent = generateEntityContent(
|
||||
newTableName = in.NewTableNames[i]
|
||||
entityFilePath = gfile.Join(dirPathEntity, gstr.CaseSnake(newTableName)+".go")
|
||||
structDefinition, appendImports = generateStructDefinition(ctx, generateStructDefinitionInput{
|
||||
CGenDaoInternalInput: in,
|
||||
TableName: tableName,
|
||||
StructName: gstr.CaseCamel(newTableName),
|
||||
FieldMap: fieldMap,
|
||||
IsDo: false,
|
||||
})
|
||||
entityContent = generateEntityContent(
|
||||
ctx,
|
||||
in,
|
||||
newTableName,
|
||||
gstr.CaseCamel(newTableName),
|
||||
generateStructDefinition(ctx, generateStructDefinitionInput{
|
||||
CGenDaoInternalInput: in,
|
||||
TableName: tableName,
|
||||
StructName: gstr.CaseCamel(newTableName),
|
||||
FieldMap: fieldMap,
|
||||
IsDo: false,
|
||||
}),
|
||||
structDefinition,
|
||||
appendImports,
|
||||
)
|
||||
)
|
||||
|
||||
err = gfile.PutContents(entityFilePath, strings.TrimSpace(entityContent))
|
||||
if err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", entityFilePath, err)
|
||||
@@ -50,15 +61,18 @@ func generateEntity(ctx context.Context, in CGenDaoInternalInput) {
|
||||
}
|
||||
}
|
||||
|
||||
func generateEntityContent(in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
|
||||
func generateEntityContent(
|
||||
ctx context.Context, in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string, appendImports []string,
|
||||
) string {
|
||||
entityContent := gstr.ReplaceByMap(
|
||||
getTemplateFromPathOrDefault(in.TplDaoEntityPath, consts.TemplateGenDaoEntityContent),
|
||||
g.MapStrStr{
|
||||
tplVarTableName: tableName,
|
||||
tplVarPackageImports: getImportPartContent(structDefine, false),
|
||||
tplVarPackageImports: getImportPartContent(ctx, structDefine, false, appendImports),
|
||||
tplVarTableNameCamelCase: tableNameCamelCase,
|
||||
tplVarStructDefine: structDefine,
|
||||
})
|
||||
},
|
||||
)
|
||||
entityContent = replaceDefaultVar(in, entityContent)
|
||||
return entityContent
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gendao
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
type generateStructDefinitionInput struct {
|
||||
@@ -20,13 +27,18 @@ type generateStructDefinitionInput struct {
|
||||
IsDo bool // Is generating DTO struct.
|
||||
}
|
||||
|
||||
func generateStructDefinition(ctx context.Context, in generateStructDefinitionInput) string {
|
||||
func generateStructDefinition(ctx context.Context, in generateStructDefinitionInput) (string, []string) {
|
||||
var appendImports []string
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
array := make([][]string, len(in.FieldMap))
|
||||
names := sortFieldKeyForDao(in.FieldMap)
|
||||
for index, name := range names {
|
||||
var imports string
|
||||
field := in.FieldMap[name]
|
||||
array[index] = generateStructFieldDefinition(ctx, field, in)
|
||||
array[index], imports = generateStructFieldDefinition(ctx, field, in)
|
||||
if imports != "" {
|
||||
appendImports = append(appendImports, imports)
|
||||
}
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
@@ -47,21 +59,42 @@ func generateStructDefinition(ctx context.Context, in generateStructDefinitionIn
|
||||
}
|
||||
buffer.WriteString(stContent)
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
return buffer.String(), appendImports
|
||||
}
|
||||
|
||||
// generateStructFieldDefinition generates and returns the attribute definition for specified field.
|
||||
func generateStructFieldDefinition(
|
||||
ctx context.Context, field *gdb.TableField, in generateStructDefinitionInput,
|
||||
) []string {
|
||||
) (attrLines []string, appendImport string) {
|
||||
var (
|
||||
err error
|
||||
typeName string
|
||||
jsonTag = getJsonTagFromCase(field.Name, in.JsonCase)
|
||||
)
|
||||
typeName, err = in.DB.CheckLocalTypeForField(ctx, field.Type, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
if in.TypeMapping != nil && len(in.TypeMapping) > 0 {
|
||||
var (
|
||||
tryTypeName string
|
||||
)
|
||||
tryTypeMatch, _ := gregex.MatchString(`(.+?)\((.+)\)`, field.Type)
|
||||
if len(tryTypeMatch) == 3 {
|
||||
tryTypeName = gstr.Trim(tryTypeMatch[1])
|
||||
} else {
|
||||
tryTypeName = gstr.Split(field.Type, " ")[0]
|
||||
}
|
||||
if tryTypeName != "" {
|
||||
if typeMapping, ok := in.TypeMapping[strings.ToLower(tryTypeName)]; ok {
|
||||
typeName = typeMapping.Type
|
||||
appendImport = typeMapping.Import
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if typeName == "" {
|
||||
typeName, err = in.DB.CheckLocalTypeForField(ctx, field.Type, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
switch typeName {
|
||||
case gdb.LocalTypeDate, gdb.LocalTypeDatetime:
|
||||
@@ -87,19 +120,18 @@ func generateStructFieldDefinition(
|
||||
}
|
||||
|
||||
var (
|
||||
tagKey = "`"
|
||||
result = []string{
|
||||
" #" + gstr.CaseCamel(field.Name),
|
||||
" #" + typeName,
|
||||
}
|
||||
tagKey = "`"
|
||||
descriptionTag = gstr.Replace(formatComment(field.Comment), `"`, `\"`)
|
||||
)
|
||||
attrLines = []string{
|
||||
" #" + gstr.CaseCamel(field.Name),
|
||||
" #" + typeName,
|
||||
}
|
||||
attrLines = append(attrLines, " #"+fmt.Sprintf(tagKey+`json:"%s"`, jsonTag))
|
||||
attrLines = append(attrLines, " #"+fmt.Sprintf(`description:"%s"`+tagKey, descriptionTag))
|
||||
attrLines = append(attrLines, " #"+fmt.Sprintf(`// %s`, formatComment(field.Comment)))
|
||||
|
||||
result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, jsonTag))
|
||||
result = append(result, " #"+fmt.Sprintf(`description:"%s"`+tagKey, descriptionTag))
|
||||
result = append(result, " #"+fmt.Sprintf(`// %s`, formatComment(field.Comment)))
|
||||
|
||||
for k, v := range result {
|
||||
for k, v := range attrLines {
|
||||
if in.NoJsonTag {
|
||||
v, _ = gregex.ReplaceString(`json:".+"`, ``, v)
|
||||
}
|
||||
@@ -109,9 +141,9 @@ func generateStructFieldDefinition(
|
||||
if in.NoModelComment {
|
||||
v, _ = gregex.ReplaceString(`//.+`, ``, v)
|
||||
}
|
||||
result[k] = v
|
||||
attrLines[k] = v
|
||||
}
|
||||
return result
|
||||
return attrLines, appendImport
|
||||
}
|
||||
|
||||
// formatComment formats the comment string to fit the golang code without any lines.
|
||||
|
||||
@@ -21,10 +21,10 @@ import (
|
||||
type (
|
||||
CGenEnums struct{}
|
||||
CGenEnumsInput struct {
|
||||
g.Meta `name:"enums" config:"{CGenEnumsConfig}" brief:"{CGenEnumsBrief}" eg:"{CGenEnumsEg}"`
|
||||
Src string `name:"src" short:"s" dc:"source folder path to be parsed" d:"."`
|
||||
Path string `name:"path" short:"p" dc:"output go file path storing enums content" d:"internal/boot/boot_enums.go"`
|
||||
Prefix string `name:"prefix" short:"x" dc:"only exports packages that starts with specified prefix"`
|
||||
g.Meta `name:"enums" config:"{CGenEnumsConfig}" brief:"{CGenEnumsBrief}" eg:"{CGenEnumsEg}"`
|
||||
Src string `name:"src" short:"s" dc:"source folder path to be parsed" d:"."`
|
||||
Path string `name:"path" short:"p" dc:"output go file path storing enums content" d:"internal/boot/boot_enums.go"`
|
||||
Prefixes []string `name:"prefixes" short:"x" dc:"only exports packages that starts with specified prefixes"`
|
||||
}
|
||||
CGenEnumsOutput struct{}
|
||||
)
|
||||
@@ -57,7 +57,7 @@ func (c CGenEnums) Enums(ctx context.Context, in CGenEnumsInput) (out *CGenEnums
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
mlog.Printf(`scanning: %s`, realPath)
|
||||
mlog.Printf(`scanning for enums: %s`, realPath)
|
||||
cfg := &packages.Config{
|
||||
Dir: realPath,
|
||||
Mode: pkgLoadMode,
|
||||
@@ -67,7 +67,7 @@ func (c CGenEnums) Enums(ctx context.Context, in CGenEnumsInput) (out *CGenEnums
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
p := NewEnumsParser(in.Prefix)
|
||||
p := NewEnumsParser(in.Prefixes)
|
||||
p.ParsePackages(pkgs)
|
||||
var enumsContent = gstr.ReplaceByMap(consts.TemplateGenEnums, g.MapStrStr{
|
||||
"{PackageName}": gfile.Basename(gfile.Dir(in.Path)),
|
||||
@@ -77,7 +77,7 @@ func (c CGenEnums) Enums(ctx context.Context, in CGenEnumsInput) (out *CGenEnums
|
||||
if err = gfile.PutContents(in.Path, enumsContent); err != nil {
|
||||
return
|
||||
}
|
||||
mlog.Printf(`generated: %s`, in.Path)
|
||||
mlog.Printf(`generated enums go file: %s`, in.Path)
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ const pkgLoadMode = 0xffffff
|
||||
type EnumsParser struct {
|
||||
enums []EnumItem
|
||||
parsedPkg map[string]struct{}
|
||||
prefix string
|
||||
prefixes []string
|
||||
}
|
||||
|
||||
type EnumItem struct {
|
||||
@@ -33,21 +33,21 @@ type EnumItem struct {
|
||||
|
||||
var standardPackages = make(map[string]struct{})
|
||||
|
||||
//func init() {
|
||||
// stdPackages, err := packages.Load(nil, "std")
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// for _, p := range stdPackages {
|
||||
// standardPackages[p.ID] = struct{}{}
|
||||
// }
|
||||
//}
|
||||
func init() {
|
||||
stdPackages, err := packages.Load(nil, "std")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, p := range stdPackages {
|
||||
standardPackages[p.ID] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func NewEnumsParser(prefix string) *EnumsParser {
|
||||
func NewEnumsParser(prefixes []string) *EnumsParser {
|
||||
return &EnumsParser{
|
||||
enums: make([]EnumItem, 0),
|
||||
parsedPkg: make(map[string]struct{}),
|
||||
prefix: prefix,
|
||||
prefixes: prefixes,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +67,16 @@ func (p *EnumsParser) ParsePackage(pkg *packages.Package) {
|
||||
return
|
||||
}
|
||||
p.parsedPkg[pkg.ID] = struct{}{}
|
||||
// Only parse specified prefix.
|
||||
if p.prefix != "" {
|
||||
if !gstr.HasPrefix(pkg.ID, p.prefix) {
|
||||
|
||||
// Only parse specified prefixes.
|
||||
if len(p.prefixes) > 0 {
|
||||
var hasPrefix bool
|
||||
for _, prefix := range p.prefixes {
|
||||
if hasPrefix = gstr.HasPrefix(pkg.ID, prefix); hasPrefix {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasPrefix {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -108,7 +115,6 @@ func (p *EnumsParser) ParsePackage(pkg *packages.Package) {
|
||||
Type: enumType,
|
||||
Kind: enumKind,
|
||||
})
|
||||
|
||||
}
|
||||
for _, im := range pkg.Imports {
|
||||
p.ParsePackage(im)
|
||||
|
||||
128
server/internal/library/hggen/internal/cmd/genpb/genpb.go
Normal file
128
server/internal/library/hggen/internal/cmd/genpb/genpb.go
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genpb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
|
||||
type (
|
||||
CGenPb struct{}
|
||||
CGenPbInput struct {
|
||||
g.Meta `name:"pb" config:"{CGenPbConfig}" brief:"{CGenPbBrief}" eg:"{CGenPbEg}"`
|
||||
Path string `name:"path" short:"p" dc:"protobuf file folder path" d:"manifest/protobuf"`
|
||||
OutputApi string `name:"api" short:"a" dc:"output folder path storing generated go files of api" d:"api"`
|
||||
OutputCtrl string `name:"ctrl" short:"c" dc:"output folder path storing generated go files of controller" d:"internal/controller"`
|
||||
}
|
||||
CGenPbOutput struct{}
|
||||
)
|
||||
|
||||
const (
|
||||
CGenPbConfig = `gfcli.gen.pb`
|
||||
CGenPbBrief = `parse proto files and generate protobuf go files`
|
||||
CGenPbEg = `
|
||||
gf gen pb
|
||||
gf gen pb -p . -a . -p .
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`CGenPbEg`: CGenPbEg,
|
||||
`CGenPbBrief`: CGenPbBrief,
|
||||
`CGenPbConfig`: CGenPbConfig,
|
||||
})
|
||||
}
|
||||
|
||||
func (c CGenPb) Pb(ctx context.Context, in CGenPbInput) (out *CGenPbOutput, err error) {
|
||||
// Necessary check.
|
||||
protoc := gproc.SearchBinary("protoc")
|
||||
if protoc == "" {
|
||||
mlog.Fatalf(`command "protoc" not found in your environment, please install protoc first: https://grpc.io/docs/languages/go/quickstart/`)
|
||||
}
|
||||
|
||||
// protocol fold checks.
|
||||
var (
|
||||
protoPath = gfile.RealPath(in.Path)
|
||||
isParsingPWD bool
|
||||
)
|
||||
if protoPath == "" {
|
||||
// Use current working directory as protoPath if there are proto files under.
|
||||
currentPath := gfile.Pwd()
|
||||
currentFiles, _ := gfile.ScanDirFile(currentPath, "*.proto")
|
||||
if len(currentFiles) > 0 {
|
||||
protoPath = currentPath
|
||||
isParsingPWD = true
|
||||
} else {
|
||||
mlog.Fatalf(`proto files folder "%s" does not exist`, in.Path)
|
||||
}
|
||||
}
|
||||
// output path checks.
|
||||
outputApiPath := gfile.RealPath(in.OutputApi)
|
||||
if outputApiPath == "" {
|
||||
if isParsingPWD {
|
||||
outputApiPath = protoPath
|
||||
} else {
|
||||
mlog.Fatalf(`output api folder "%s" does not exist`, in.OutputApi)
|
||||
}
|
||||
}
|
||||
outputCtrlPath := gfile.RealPath(in.OutputCtrl)
|
||||
if outputCtrlPath == "" {
|
||||
if isParsingPWD {
|
||||
outputCtrlPath = ""
|
||||
} else {
|
||||
mlog.Fatalf(`output controller folder "%s" does not exist`, in.OutputCtrl)
|
||||
}
|
||||
}
|
||||
|
||||
// folder scanning.
|
||||
files, err := gfile.ScanDirFile(protoPath, "*.proto", true)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if len(files) == 0 {
|
||||
mlog.Fatalf(`no proto files found in folder "%s"`, in.Path)
|
||||
}
|
||||
|
||||
if err = gfile.Chdir(protoPath); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
for _, file := range files {
|
||||
var command = gproc.NewProcess(protoc, nil)
|
||||
command.Args = append(command.Args, "--proto_path="+gfile.Pwd())
|
||||
command.Args = append(command.Args, "--go_out=paths=source_relative:"+outputApiPath)
|
||||
command.Args = append(command.Args, "--go-grpc_out=paths=source_relative:"+outputApiPath)
|
||||
command.Args = append(command.Args, file)
|
||||
mlog.Print(command.String())
|
||||
if err = command.Run(ctx); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
}
|
||||
// Generate struct tag according comment rules.
|
||||
err = c.generateStructTag(ctx, generateStructTagInput{OutputApiPath: outputApiPath})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Generate controllers according comment rules.
|
||||
if outputCtrlPath != "" {
|
||||
err = c.generateController(ctx, generateControllerInput{
|
||||
OutputApiPath: outputApiPath,
|
||||
OutputCtrlPath: outputCtrlPath,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genpb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"hotgo/internal/library/hggen/internal/utility/utils"
|
||||
)
|
||||
|
||||
type generateControllerInput struct {
|
||||
OutputApiPath string
|
||||
OutputCtrlPath string
|
||||
}
|
||||
|
||||
type generateCtrl struct {
|
||||
Name string
|
||||
Package string
|
||||
Version string
|
||||
Methods []generateCtrlMethod
|
||||
}
|
||||
|
||||
type generateCtrlMethod struct {
|
||||
Name string
|
||||
Definition string
|
||||
}
|
||||
|
||||
const (
|
||||
controllerTemplate = `
|
||||
package {Package}
|
||||
|
||||
type Controller struct {
|
||||
{Version}.Unimplemented{Name}Server
|
||||
}
|
||||
|
||||
func Register(s *grpcx.GrpcServer) {
|
||||
{Version}.Register{Name}Server(s.Server, &Controller{})
|
||||
}
|
||||
`
|
||||
controllerMethodTemplate = `
|
||||
func (*Controller) {Definition} {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
func (c CGenPb) generateController(ctx context.Context, in generateControllerInput) (err error) {
|
||||
files, err := gfile.ScanDirFile(in.OutputApiPath, "*_grpc.pb.go", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var controllers []generateCtrl
|
||||
for _, file := range files {
|
||||
fileControllers, err := c.parseControllers(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
controllers = append(controllers, fileControllers...)
|
||||
}
|
||||
if len(controllers) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Generate controller files.
|
||||
err = c.doGenerateControllers(in, controllers)
|
||||
return
|
||||
}
|
||||
|
||||
func (c CGenPb) parseControllers(filePath string) ([]generateCtrl, error) {
|
||||
var (
|
||||
controllers []generateCtrl
|
||||
content = gfile.GetContents(filePath)
|
||||
)
|
||||
_, err := gregex.ReplaceStringFuncMatch(
|
||||
`type (\w+)Server interface {([\s\S]+?)}`,
|
||||
content,
|
||||
func(match []string) string {
|
||||
ctrl := generateCtrl{
|
||||
Name: match[1],
|
||||
Package: strings.ReplaceAll(gfile.Basename(gfile.Dir(gfile.Dir(filePath))), "-", "_"),
|
||||
Version: gfile.Basename(gfile.Dir(filePath)),
|
||||
Methods: make([]generateCtrlMethod, 0),
|
||||
}
|
||||
lines := gstr.Split(match[2], "\n")
|
||||
for _, line := range lines {
|
||||
line = gstr.Trim(line)
|
||||
if line == "" || !gstr.IsLetterUpper(line[0]) {
|
||||
continue
|
||||
}
|
||||
// Comment.
|
||||
if gregex.IsMatchString(`^//.+`, line) {
|
||||
continue
|
||||
}
|
||||
line, _ = gregex.ReplaceStringFuncMatch(
|
||||
`^(\w+)\(context\.Context, \*(\w+)\) \(\*(\w+), error\)$`,
|
||||
line,
|
||||
func(match []string) string {
|
||||
return fmt.Sprintf(
|
||||
`%s(ctx context.Context, req *%s.%s) (res *%s.%s, err error)`,
|
||||
match[1], ctrl.Version, match[2], ctrl.Version, match[3],
|
||||
)
|
||||
},
|
||||
)
|
||||
ctrl.Methods = append(ctrl.Methods, generateCtrlMethod{
|
||||
Name: gstr.Split(line, "(")[0],
|
||||
Definition: line,
|
||||
})
|
||||
}
|
||||
if len(ctrl.Methods) > 0 {
|
||||
controllers = append(controllers, ctrl)
|
||||
}
|
||||
return match[0]
|
||||
},
|
||||
)
|
||||
return controllers, err
|
||||
}
|
||||
|
||||
func (c CGenPb) doGenerateControllers(in generateControllerInput, controllers []generateCtrl) (err error) {
|
||||
for _, controller := range controllers {
|
||||
err = c.doGenerateController(in, controller)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = utils.ReplaceGeneratedContentGFV2(in.OutputCtrlPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c CGenPb) doGenerateController(in generateControllerInput, controller generateCtrl) (err error) {
|
||||
var (
|
||||
folderPath = gfile.Join(in.OutputCtrlPath, controller.Package)
|
||||
filePath = gfile.Join(folderPath, controller.Package+".go")
|
||||
isDirty bool
|
||||
)
|
||||
if !gfile.Exists(folderPath) {
|
||||
if err = gfile.Mkdir(folderPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !gfile.Exists(filePath) {
|
||||
templateContent := gstr.ReplaceByMap(controllerTemplate, g.MapStrStr{
|
||||
"{Name}": controller.Name,
|
||||
"{Version}": controller.Version,
|
||||
"{Package}": controller.Package,
|
||||
})
|
||||
if err = gfile.PutContents(filePath, templateContent); err != nil {
|
||||
return err
|
||||
}
|
||||
isDirty = true
|
||||
}
|
||||
// Exist controller content.
|
||||
var ctrlContent string
|
||||
files, err := gfile.ScanDirFile(folderPath, "*.go", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
if ctrlContent != "" {
|
||||
ctrlContent += "\n"
|
||||
}
|
||||
ctrlContent += gfile.GetContents(file)
|
||||
}
|
||||
// Generate method content.
|
||||
var generatedContent string
|
||||
for _, method := range controller.Methods {
|
||||
if gstr.Contains(ctrlContent, fmt.Sprintf(`%s(`, method.Name)) {
|
||||
continue
|
||||
}
|
||||
if generatedContent != "" {
|
||||
generatedContent += "\n"
|
||||
}
|
||||
generatedContent += gstr.ReplaceByMap(controllerMethodTemplate, g.MapStrStr{
|
||||
"{Definition}": method.Definition,
|
||||
})
|
||||
}
|
||||
if generatedContent != "" {
|
||||
err = gfile.PutContentsAppend(filePath, generatedContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isDirty = true
|
||||
}
|
||||
if isDirty {
|
||||
utils.GoFmt(filePath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
113
server/internal/library/hggen/internal/cmd/genpb/genpb_tag.go
Normal file
113
server/internal/library/hggen/internal/cmd/genpb/genpb_tag.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genpb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"hotgo/internal/library/hggen/internal/utility/utils"
|
||||
)
|
||||
|
||||
type generateStructTagInput struct {
|
||||
OutputApiPath string
|
||||
}
|
||||
|
||||
func (c CGenPb) generateStructTag(ctx context.Context, in generateStructTagInput) (err error) {
|
||||
files, err := gfile.ScanDirFile(in.OutputApiPath, "*.pb.go", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var content string
|
||||
for _, file := range files {
|
||||
content = gfile.GetContents(file)
|
||||
content, err = c.doTagReplacement(ctx, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = gfile.PutContents(file, content); err != nil {
|
||||
return err
|
||||
}
|
||||
utils.GoFmt(file)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c CGenPb) doTagReplacement(ctx context.Context, content string) (string, error) {
|
||||
content, err := gregex.ReplaceStringFuncMatch(`type (\w+) struct {([\s\S]+?)}`, content, func(match []string) string {
|
||||
var (
|
||||
topCommentMatch []string
|
||||
tailCommentMatch []string
|
||||
lines = gstr.Split(match[2], "\n")
|
||||
lineTagMap = gmap.NewListMap()
|
||||
)
|
||||
for index, line := range lines {
|
||||
line = gstr.Trim(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
// Top comment.
|
||||
topCommentMatch, _ = gregex.MatchString(`^/[/|\*](.+)`, line)
|
||||
if len(topCommentMatch) > 1 {
|
||||
c.tagCommentIntoListMap(gstr.Trim(topCommentMatch[1]), lineTagMap)
|
||||
continue
|
||||
}
|
||||
// Tail comment.
|
||||
tailCommentMatch, _ = gregex.MatchString(".+?`.+?`.+?//(.+)", line)
|
||||
if len(tailCommentMatch) > 1 {
|
||||
c.tagCommentIntoListMap(gstr.Trim(tailCommentMatch[1]), lineTagMap)
|
||||
}
|
||||
// Tag injection.
|
||||
if !lineTagMap.IsEmpty() {
|
||||
tagContent := c.listMapToStructTag(lineTagMap)
|
||||
lineTagMap.Clear()
|
||||
line, _ = gregex.ReplaceString("`(.+)`", fmt.Sprintf("`$1 %s`", tagContent), line)
|
||||
}
|
||||
lines[index] = line
|
||||
}
|
||||
match[2] = gstr.Join(lines, "\n")
|
||||
return fmt.Sprintf("type %s struct {%s}", match[1], match[2])
|
||||
})
|
||||
return content, err
|
||||
}
|
||||
|
||||
func (c CGenPb) tagCommentIntoListMap(comment string, lineTagMap *gmap.ListMap) {
|
||||
tagCommentMatch, _ := gregex.MatchString(`^(\w+):(.+)`, comment)
|
||||
if len(tagCommentMatch) > 1 {
|
||||
var (
|
||||
tagName = gstr.Trim(tagCommentMatch[1])
|
||||
tagContent = gstr.Trim(tagCommentMatch[2])
|
||||
)
|
||||
lineTagMap.Set(tagName, lineTagMap.GetVar(tagName).String()+tagContent)
|
||||
} else {
|
||||
var (
|
||||
tagName = "dc"
|
||||
tagContent = comment
|
||||
)
|
||||
lineTagMap.Set(tagName, lineTagMap.GetVar(tagName).String()+tagContent)
|
||||
}
|
||||
}
|
||||
|
||||
func (c CGenPb) listMapToStructTag(lineTagMap *gmap.ListMap) string {
|
||||
var tag string
|
||||
lineTagMap.Iterator(func(key, value interface{}) bool {
|
||||
if tag != "" {
|
||||
tag += " "
|
||||
}
|
||||
tag += fmt.Sprintf(
|
||||
`%s:"%s"`,
|
||||
key, gstr.Replace(gconv.String(value), `"`, `\"`),
|
||||
)
|
||||
return true
|
||||
})
|
||||
return tag
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genpbentity
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"hotgo/internal/library/hggen/internal/consts"
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
|
||||
type (
|
||||
CGenPbEntity struct{}
|
||||
CGenPbEntityInput struct {
|
||||
g.Meta `name:"pbentity" config:"{CGenPbEntityConfig}" brief:"{CGenPbEntityBrief}" eg:"{CGenPbEntityEg}" ad:"{CGenPbEntityAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{CGenPbEntityBriefPath}" d:"manifest/protobuf/pbentity"`
|
||||
Package string `name:"package" short:"k" brief:"{CGenPbEntityBriefPackage}"`
|
||||
Link string `name:"link" short:"l" brief:"{CGenPbEntityBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{CGenPbEntityBriefTables}"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{CGenPbEntityBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenPbEntityBriefRemovePrefix}"`
|
||||
NameCase string `name:"nameCase" short:"n" brief:"{CGenPbEntityBriefNameCase}" d:"Camel"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenPbEntityBriefJsonCase}" d:"CamelLower"`
|
||||
Option string `name:"option" short:"o" brief:"{CGenPbEntityBriefOption}"`
|
||||
}
|
||||
CGenPbEntityOutput struct{}
|
||||
|
||||
CGenPbEntityInternalInput struct {
|
||||
CGenPbEntityInput
|
||||
DB gdb.DB
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPackageSuffix = `api/pbentity`
|
||||
CGenPbEntityConfig = `gfcli.gen.pbentity`
|
||||
CGenPbEntityBrief = `generate entity message files in protobuf3 format`
|
||||
CGenPbEntityEg = `
|
||||
gf gen pbentity
|
||||
gf gen pbentity -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
gf gen pbentity -p ./protocol/demos/entity -t user,user_detail,user_login
|
||||
gf gen pbentity -r user_ -k github.com/gogf/gf/example/protobuf
|
||||
gf gen pbentity -r user_
|
||||
`
|
||||
|
||||
CGenPbEntityAd = `
|
||||
CONFIGURATION SUPPORT
|
||||
Options are also supported by configuration file.
|
||||
It's suggested using configuration file instead of command line arguments making producing.
|
||||
The configuration node name is "gf.gen.pbentity", which also supports multiple databases, for example(config.yaml):
|
||||
gfcli:
|
||||
gen:
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
path: "protocol/demos/entity"
|
||||
tables: "order,products"
|
||||
package: "demos"
|
||||
- pbentity:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
|
||||
path: "protocol/demos/entity"
|
||||
prefix: "primary_"
|
||||
tables: "user, userDetail"
|
||||
package: "demos"
|
||||
option: |
|
||||
option go_package = "protobuf/demos";
|
||||
option java_package = "protobuf/demos";
|
||||
option php_namespace = "protobuf/demos";
|
||||
`
|
||||
CGenPbEntityBriefPath = `directory path for generated files storing`
|
||||
CGenPbEntityBriefPackage = `package path for all entity proto files`
|
||||
CGenPbEntityBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
CGenPbEntityBriefTables = `generate models only for given tables, multiple table names separated with ','`
|
||||
CGenPbEntityBriefPrefix = `add specified prefix for all entity names and entity proto files`
|
||||
CGenPbEntityBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
|
||||
CGenPbEntityBriefOption = `extra protobuf options`
|
||||
CGenPbEntityBriefGroup = `
|
||||
specifying the configuration group name of database for generated ORM instance,
|
||||
it's not necessary and the default value is "default"
|
||||
`
|
||||
|
||||
CGenPbEntityBriefNameCase = `
|
||||
case for message attribute names, default is "Camel":
|
||||
| Case | Example |
|
||||
|---------------- |--------------------|
|
||||
| Camel | AnyKindOfString |
|
||||
| CamelLower | anyKindOfString | default
|
||||
| Snake | any_kind_of_string |
|
||||
| SnakeScreaming | ANY_KIND_OF_STRING |
|
||||
| SnakeFirstUpper | rgb_code_md5 |
|
||||
| Kebab | any-kind-of-string |
|
||||
| KebabScreaming | ANY-KIND-OF-STRING |
|
||||
`
|
||||
|
||||
CGenPbEntityBriefJsonCase = `
|
||||
case for message json tag, cases are the same as "nameCase", default "CamelLower".
|
||||
set it to "none" to ignore json tag generating.
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`CGenPbEntityConfig`: CGenPbEntityConfig,
|
||||
`CGenPbEntityBrief`: CGenPbEntityBrief,
|
||||
`CGenPbEntityEg`: CGenPbEntityEg,
|
||||
`CGenPbEntityAd`: CGenPbEntityAd,
|
||||
`CGenPbEntityBriefPath`: CGenPbEntityBriefPath,
|
||||
`CGenPbEntityBriefPackage`: CGenPbEntityBriefPackage,
|
||||
`CGenPbEntityBriefLink`: CGenPbEntityBriefLink,
|
||||
`CGenPbEntityBriefTables`: CGenPbEntityBriefTables,
|
||||
`CGenPbEntityBriefPrefix`: CGenPbEntityBriefPrefix,
|
||||
`CGenPbEntityBriefRemovePrefix`: CGenPbEntityBriefRemovePrefix,
|
||||
`CGenPbEntityBriefGroup`: CGenPbEntityBriefGroup,
|
||||
`CGenPbEntityBriefNameCase`: CGenPbEntityBriefNameCase,
|
||||
`CGenPbEntityBriefJsonCase`: CGenPbEntityBriefJsonCase,
|
||||
`CGenPbEntityBriefOption`: CGenPbEntityBriefOption,
|
||||
})
|
||||
}
|
||||
|
||||
func (c CGenPbEntity) PbEntity(ctx context.Context, in CGenPbEntityInput) (out *CGenPbEntityOutput, err error) {
|
||||
var (
|
||||
config = g.Cfg()
|
||||
)
|
||||
if config.Available(ctx) {
|
||||
v := config.MustGet(ctx, CGenPbEntityConfig)
|
||||
if v.IsSlice() {
|
||||
for i := 0; i < len(v.Interfaces()); i++ {
|
||||
doGenPbEntityForArray(ctx, i, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
} else {
|
||||
doGenPbEntityForArray(ctx, -1, in)
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
func doGenPbEntityForArray(ctx context.Context, index int, in CGenPbEntityInput) {
|
||||
var (
|
||||
err error
|
||||
db gdb.DB
|
||||
)
|
||||
if index >= 0 {
|
||||
err = g.Cfg().MustGet(
|
||||
ctx,
|
||||
fmt.Sprintf(`%s.%d`, CGenPbEntityConfig, index),
|
||||
).Scan(&in)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`invalid configuration of "%s": %+v`, CGenPbEntityConfig, err)
|
||||
}
|
||||
}
|
||||
if in.Package == "" {
|
||||
mlog.Debug(`package parameter is empty, trying calculating the package path using go.mod`)
|
||||
if !gfile.Exists("go.mod") {
|
||||
mlog.Fatal("go.mod does not exist in current working directory")
|
||||
}
|
||||
var (
|
||||
modName string
|
||||
goModContent = gfile.GetContents("go.mod")
|
||||
match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
modName = gstr.Trim(match[1])
|
||||
in.Package = modName + "/" + defaultPackageSuffix
|
||||
} else {
|
||||
mlog.Fatal("module name does not found in go.mod")
|
||||
}
|
||||
}
|
||||
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
|
||||
// It uses user passed database configuration.
|
||||
if in.Link != "" {
|
||||
var (
|
||||
tempGroup = gtime.TimestampNanoStr()
|
||||
match, _ = gregex.MatchString(`([a-z]+):(.+)`, in.Link)
|
||||
)
|
||||
if len(match) == 3 {
|
||||
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
|
||||
Type: gstr.Trim(match[1]),
|
||||
Link: gstr.Trim(match[2]),
|
||||
})
|
||||
db, _ = gdb.Instance(tempGroup)
|
||||
}
|
||||
} else {
|
||||
db = g.DB()
|
||||
}
|
||||
if db == nil {
|
||||
mlog.Fatal("database initialization failed")
|
||||
}
|
||||
|
||||
tableNames := ([]string)(nil)
|
||||
if in.Tables != "" {
|
||||
tableNames = gstr.SplitAndTrim(in.Tables, ",")
|
||||
} else {
|
||||
tableNames, err = db.Tables(context.TODO())
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables failed: \n %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tableName := range tableNames {
|
||||
newTableName := tableName
|
||||
for _, v := range removePrefixArray {
|
||||
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
|
||||
}
|
||||
generatePbEntityContentFile(ctx, CGenPbEntityInternalInput{
|
||||
CGenPbEntityInput: in,
|
||||
DB: db,
|
||||
TableName: tableName,
|
||||
NewTableName: newTableName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// generatePbEntityContentFile generates the protobuf files for given table.
|
||||
func generatePbEntityContentFile(ctx context.Context, in CGenPbEntityInternalInput) {
|
||||
fieldMap, err := in.DB.TableFields(ctx, in.TableName)
|
||||
if err != nil {
|
||||
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
|
||||
}
|
||||
// Change the `newTableName` if `Prefix` is given.
|
||||
newTableName := in.Prefix + in.NewTableName
|
||||
var (
|
||||
imports string
|
||||
tableNameCamelCase = gstr.CaseCamel(newTableName)
|
||||
tableNameSnakeCase = gstr.CaseSnake(newTableName)
|
||||
entityMessageDefine = generateEntityMessageDefinition(tableNameCamelCase, fieldMap, in)
|
||||
fileName = gstr.Trim(tableNameSnakeCase, "-_.")
|
||||
path = gfile.Join(in.Path, fileName+".proto")
|
||||
)
|
||||
if gstr.Contains(entityMessageDefine, "google.protobuf.Timestamp") {
|
||||
imports = `import "google/protobuf/timestamp.proto";`
|
||||
}
|
||||
entityContent := gstr.ReplaceByMap(getTplPbEntityContent(""), g.MapStrStr{
|
||||
"{Imports}": imports,
|
||||
"{PackageName}": gfile.Basename(in.Package),
|
||||
"{GoPackage}": in.Package,
|
||||
"{OptionContent}": in.Option,
|
||||
"{EntityMessage}": entityMessageDefine,
|
||||
})
|
||||
if err := gfile.PutContents(path, strings.TrimSpace(entityContent)); err != nil {
|
||||
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
|
||||
} else {
|
||||
mlog.Print("generated:", path)
|
||||
}
|
||||
}
|
||||
|
||||
// generateEntityMessageDefinition generates and returns the message definition for specified table.
|
||||
func generateEntityMessageDefinition(entityName string, fieldMap map[string]*gdb.TableField, in CGenPbEntityInternalInput) string {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
array = make([][]string, len(fieldMap))
|
||||
names = sortFieldKeyForPbEntity(fieldMap)
|
||||
)
|
||||
for index, name := range names {
|
||||
array[index] = generateMessageFieldForPbEntity(index+1, fieldMap[name], in)
|
||||
}
|
||||
tw := tablewriter.NewWriter(buffer)
|
||||
tw.SetBorder(false)
|
||||
tw.SetRowLine(false)
|
||||
tw.SetAutoWrapText(false)
|
||||
tw.SetColumnSeparator("")
|
||||
tw.AppendBulk(array)
|
||||
tw.Render()
|
||||
stContent := buffer.String()
|
||||
// Let's do this hack of table writer for indent!
|
||||
stContent = gstr.Replace(stContent, " #", "")
|
||||
buffer.Reset()
|
||||
buffer.WriteString(fmt.Sprintf("message %s {\n", entityName))
|
||||
buffer.WriteString(stContent)
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// generateMessageFieldForPbEntity generates and returns the message definition for specified field.
|
||||
func generateMessageFieldForPbEntity(index int, field *gdb.TableField, in CGenPbEntityInternalInput) []string {
|
||||
var (
|
||||
typeName string
|
||||
comment string
|
||||
jsonTagStr string
|
||||
err error
|
||||
ctx = gctx.GetInitCtx()
|
||||
)
|
||||
typeName, err = in.DB.CheckLocalTypeForField(ctx, field.Type, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var typeMapping = map[string]string{
|
||||
gdb.LocalTypeString: "string",
|
||||
gdb.LocalTypeDate: "google.protobuf.Timestamp",
|
||||
gdb.LocalTypeDatetime: "google.protobuf.Timestamp",
|
||||
gdb.LocalTypeInt: "int32",
|
||||
gdb.LocalTypeUint: "uint32",
|
||||
gdb.LocalTypeInt64: "int64",
|
||||
gdb.LocalTypeUint64: "uint64",
|
||||
gdb.LocalTypeIntSlice: "repeated int32",
|
||||
gdb.LocalTypeInt64Slice: "repeated int64",
|
||||
gdb.LocalTypeUint64Slice: "repeated uint64",
|
||||
gdb.LocalTypeInt64Bytes: "repeated int64",
|
||||
gdb.LocalTypeUint64Bytes: "repeated uint64",
|
||||
gdb.LocalTypeFloat32: "float",
|
||||
gdb.LocalTypeFloat64: "double",
|
||||
gdb.LocalTypeBytes: "bytes",
|
||||
gdb.LocalTypeBool: "bool",
|
||||
gdb.LocalTypeJson: "string",
|
||||
gdb.LocalTypeJsonb: "string",
|
||||
}
|
||||
typeName = typeMapping[typeName]
|
||||
if typeName == "" {
|
||||
typeName = "string"
|
||||
}
|
||||
|
||||
comment = gstr.ReplaceByArray(field.Comment, g.SliceStr{
|
||||
"\n", " ",
|
||||
"\r", " ",
|
||||
})
|
||||
comment = gstr.Trim(comment)
|
||||
comment = gstr.Replace(comment, `\n`, " ")
|
||||
comment, _ = gregex.ReplaceString(`\s{2,}`, ` `, comment)
|
||||
if jsonTagName := formatCase(field.Name, in.JsonCase); jsonTagName != "" {
|
||||
// beautiful indent.
|
||||
if index < 10 {
|
||||
// 3 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else if index < 100 {
|
||||
// 2 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
} else {
|
||||
// 1 spaces
|
||||
jsonTagStr = " " + jsonTagStr
|
||||
}
|
||||
}
|
||||
return []string{
|
||||
" #" + typeName,
|
||||
" #" + formatCase(field.Name, in.NameCase),
|
||||
" #= " + gconv.String(index) + jsonTagStr + ";",
|
||||
" #" + fmt.Sprintf(`// %s`, comment),
|
||||
}
|
||||
}
|
||||
|
||||
func getTplPbEntityContent(tplEntityPath string) string {
|
||||
if tplEntityPath != "" {
|
||||
return gfile.GetContents(tplEntityPath)
|
||||
}
|
||||
return consts.TemplatePbEntityMessageContent
|
||||
}
|
||||
|
||||
// formatCase call gstr.Case* function to convert the s to specified case.
|
||||
func formatCase(str, caseStr string) string {
|
||||
switch gstr.ToLower(caseStr) {
|
||||
case gstr.ToLower("Camel"):
|
||||
return gstr.CaseCamel(str)
|
||||
|
||||
case gstr.ToLower("CamelLower"):
|
||||
return gstr.CaseCamelLower(str)
|
||||
|
||||
case gstr.ToLower("Kebab"):
|
||||
return gstr.CaseKebab(str)
|
||||
|
||||
case gstr.ToLower("KebabScreaming"):
|
||||
return gstr.CaseKebabScreaming(str)
|
||||
|
||||
case gstr.ToLower("Snake"):
|
||||
return gstr.CaseSnake(str)
|
||||
|
||||
case gstr.ToLower("SnakeFirstUpper"):
|
||||
return gstr.CaseSnakeFirstUpper(str)
|
||||
|
||||
case gstr.ToLower("SnakeScreaming"):
|
||||
return gstr.CaseSnakeScreaming(str)
|
||||
|
||||
case "none":
|
||||
return ""
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func sortFieldKeyForPbEntity(fieldMap map[string]*gdb.TableField) []string {
|
||||
names := make(map[int]string)
|
||||
for _, field := range fieldMap {
|
||||
names[field.Index] = field.Name
|
||||
}
|
||||
var (
|
||||
result = make([]string, len(names))
|
||||
i = 0
|
||||
j = 0
|
||||
)
|
||||
for {
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
if val, ok := names[i]; ok {
|
||||
result[j] = val
|
||||
j++
|
||||
delete(names, i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genservice
|
||||
|
||||
import (
|
||||
@@ -5,10 +11,10 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
@@ -44,7 +50,7 @@ destination file name storing automatically generated go files, cases are as fol
|
||||
`
|
||||
CGenServiceBriefWatchFile = `used in file watcher, it re-generates all service go files only if given file is under srcFolder`
|
||||
CGenServiceBriefStPattern = `regular expression matching struct name for generating service. default: ^s([A-Z]\\\\w+)$`
|
||||
CGenServiceBriefPackages = `produce go files only for given source packages`
|
||||
CGenServiceBriefPackages = `produce go files only for given source packages(source folders)`
|
||||
CGenServiceBriefImportPrefix = `custom import prefix to calculate import path for generated importing go file of logic`
|
||||
CGenServiceBriefClear = `delete all generated go files that are not used any further`
|
||||
)
|
||||
@@ -87,21 +93,6 @@ const (
|
||||
)
|
||||
|
||||
func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGenServiceOutput, err error) {
|
||||
// File lock to avoid multiple processes.
|
||||
var (
|
||||
flockFilePath = gfile.Temp("gf.cli.gen.service.lock")
|
||||
flockContent = gfile.GetContents(flockFilePath)
|
||||
)
|
||||
if flockContent != "" {
|
||||
if gtime.Timestamp()-gconv.Int64(flockContent) < genServiceFileLockSeconds {
|
||||
// If another "gen service" process is running, it just exits.
|
||||
mlog.Debug(`another "gen service" process is running, exit`)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer gfile.Remove(flockFilePath)
|
||||
_ = gfile.PutContents(flockFilePath, gtime.TimestampStr())
|
||||
|
||||
in.SrcFolder = gstr.TrimRight(in.SrcFolder, `\/`)
|
||||
in.SrcFolder = gstr.Replace(in.SrcFolder, "\\", "/")
|
||||
in.WatchFile = gstr.TrimRight(in.WatchFile, `\/`)
|
||||
@@ -109,6 +100,21 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
|
||||
|
||||
// Watch file handling.
|
||||
if in.WatchFile != "" {
|
||||
// File lock to avoid multiple processes.
|
||||
var (
|
||||
flockFilePath = gfile.Temp("gf.cli.gen.service.lock")
|
||||
flockContent = gfile.GetContents(flockFilePath)
|
||||
)
|
||||
if flockContent != "" {
|
||||
if gtime.Timestamp()-gconv.Int64(flockContent) < genServiceFileLockSeconds {
|
||||
// If another "gen service" process is running, it just exits.
|
||||
mlog.Debug(`another "gen service" process is running, exit`)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer gfile.Remove(flockFilePath)
|
||||
_ = gfile.PutContents(flockFilePath, gtime.TimestampStr())
|
||||
|
||||
// It works only if given WatchFile is in SrcFolder.
|
||||
var (
|
||||
watchFileDir = gfile.Dir(in.WatchFile)
|
||||
@@ -125,13 +131,10 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
|
||||
mlog.Fatalf(`%+v`, err)
|
||||
}
|
||||
mlog.Debug("Chdir:", newWorkingDir)
|
||||
_ = gfile.Remove(flockFilePath)
|
||||
var command = fmt.Sprintf(
|
||||
`%s gen service -packages=%s`,
|
||||
gfile.SelfName(), gfile.Basename(watchFileDir),
|
||||
)
|
||||
err = gproc.ShellRun(ctx, command)
|
||||
return
|
||||
|
||||
in.WatchFile = ""
|
||||
in.Packages = []string{gfile.Basename(watchFileDir)}
|
||||
return c.Service(ctx, in)
|
||||
}
|
||||
|
||||
if !gfile.Exists(in.SrcFolder) {
|
||||
@@ -139,16 +142,7 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
|
||||
}
|
||||
|
||||
if in.ImportPrefix == "" {
|
||||
if !gfile.Exists("go.mod") {
|
||||
mlog.Fatal("ImportPrefix is empty and go.mod does not exist in current working directory")
|
||||
}
|
||||
var (
|
||||
goModContent = gfile.GetContents("go.mod")
|
||||
match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
in.ImportPrefix = fmt.Sprintf(`%s/%s`, gstr.Trim(match[1]), gstr.Replace(in.SrcFolder, `\`, `/`))
|
||||
}
|
||||
in.ImportPrefix = utils.GetImportPath(in.SrcFolder)
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -176,30 +170,118 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
|
||||
if len(files) == 0 {
|
||||
continue
|
||||
}
|
||||
// Parse single logic package folder.
|
||||
var (
|
||||
// StructName => FunctionDefinitions
|
||||
srcPkgInterfaceMap = make(map[string]*garray.StrArray)
|
||||
srcImportedPackages = garray.NewSortedStrArray().SetUnique(true)
|
||||
srcPackageName = gfile.Basename(srcFolderPath)
|
||||
ok bool
|
||||
dstFilePath = gfile.Join(in.DstFolder,
|
||||
srcPkgInterfaceMap = make(map[string]*garray.StrArray)
|
||||
srcImportedPackages = garray.NewSortedStrArray().SetUnique(true)
|
||||
importAliasToPathMap = gmap.NewStrStrMap() // for conflict imports check. alias => import path(with `"`)
|
||||
importPathToAliasMap = gmap.NewStrStrMap() // for conflict imports check. import path(with `"`) => alias
|
||||
srcPackageName = gfile.Basename(srcFolderPath)
|
||||
ok bool
|
||||
dstFilePath = gfile.Join(in.DstFolder,
|
||||
c.getDstFileNameCase(srcPackageName, in.DstFileNameCase)+".go",
|
||||
)
|
||||
srcCodeCommentedMap = make(map[string]string)
|
||||
)
|
||||
generatedDstFilePathSet.Add(dstFilePath)
|
||||
for _, file := range files {
|
||||
var packageItems []packageItem
|
||||
fileContent = gfile.GetContents(file)
|
||||
// Calculate imported packages of source go files.
|
||||
err = c.calculateImportedPackages(fileContent, srcImportedPackages)
|
||||
// Calculate code comments in source Go files.
|
||||
err = c.calculateCodeCommented(in, fileContent, srcCodeCommentedMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// remove all comments.
|
||||
fileContent, err = gregex.ReplaceString(`/[/|\*](.*)`, "", fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Calculate imported packages of source go files.
|
||||
packageItems, err = c.calculateImportedPackages(fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// try finding the conflicts imports between files.
|
||||
for _, item := range packageItems {
|
||||
var alias = item.Alias
|
||||
if alias == "" {
|
||||
alias = gfile.Basename(gstr.Trim(item.Path, `"`))
|
||||
}
|
||||
|
||||
// ignore unused import paths, which do not exist in function definitions.
|
||||
if !gregex.IsMatchString(fmt.Sprintf(`func .+?([^\w])%s(\.\w+).+?{`, alias), fileContent) {
|
||||
mlog.Debugf(`ignore unused package: %s`, item.RawImport)
|
||||
continue
|
||||
}
|
||||
|
||||
// find the exist alias with the same import path.
|
||||
var existAlias = importPathToAliasMap.Get(item.Path)
|
||||
if existAlias != "" {
|
||||
fileContent, err = gregex.ReplaceStringFuncMatch(
|
||||
fmt.Sprintf(`([^\w])%s(\.\w+)`, alias), fileContent,
|
||||
func(match []string) string {
|
||||
return match[1] + existAlias + match[2]
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// resolve alias conflicts.
|
||||
var importPath = importAliasToPathMap.Get(alias)
|
||||
if importPath == "" {
|
||||
importAliasToPathMap.Set(alias, item.Path)
|
||||
importPathToAliasMap.Set(item.Path, alias)
|
||||
srcImportedPackages.Add(item.RawImport)
|
||||
continue
|
||||
}
|
||||
if importPath != item.Path {
|
||||
// update the conflicted alias for import path with suffix.
|
||||
// eg:
|
||||
// v1 -> v10
|
||||
// v11 -> v110
|
||||
for aliasIndex := 0; ; aliasIndex++ {
|
||||
item.Alias = fmt.Sprintf(`%s%d`, alias, aliasIndex)
|
||||
var existPathForAlias = importAliasToPathMap.Get(item.Alias)
|
||||
if existPathForAlias != "" {
|
||||
if existPathForAlias == item.Path {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
importPathToAliasMap.Set(item.Path, item.Alias)
|
||||
importAliasToPathMap.Set(item.Alias, item.Path)
|
||||
// reformat the import path with alias.
|
||||
item.RawImport = fmt.Sprintf(`%s %s`, item.Alias, item.Path)
|
||||
|
||||
// update the file content with new alias import.
|
||||
fileContent, err = gregex.ReplaceStringFuncMatch(
|
||||
fmt.Sprintf(`([^\w])%s(\.\w+)`, alias), fileContent,
|
||||
func(match []string) string {
|
||||
return match[1] + item.Alias + match[2]
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srcImportedPackages.Add(item.RawImport)
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate functions and interfaces for service generating.
|
||||
err = c.calculateInterfaceFunctions(in, fileContent, srcPkgInterfaceMap, dstPackageName)
|
||||
err = c.calculateInterfaceFunctions(in, fileContent, srcPkgInterfaceMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
initImportSrcPackages = append(
|
||||
initImportSrcPackages,
|
||||
fmt.Sprintf(`%s/%s`, in.ImportPrefix, srcPackageName),
|
||||
@@ -212,7 +294,7 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
|
||||
)
|
||||
continue
|
||||
}
|
||||
// Generating service go file for logic.
|
||||
// Generating service go file for single logic package.
|
||||
if ok, err = c.generateServiceFile(generateServiceFilesInput{
|
||||
CGenServiceInput: in,
|
||||
SrcStructFunctions: srcPkgInterfaceMap,
|
||||
@@ -220,6 +302,7 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
|
||||
SrcPackageName: srcPackageName,
|
||||
DstPackageName: dstPackageName,
|
||||
DstFilePath: dstFilePath,
|
||||
SrcCodeCommentedMap: srcCodeCommentedMap,
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -254,24 +337,71 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
|
||||
}
|
||||
|
||||
// Replace v1 to v2 for GoFrame.
|
||||
if err = c.replaceGeneratedServiceContentGFV2(in); err != nil {
|
||||
if err = utils.ReplaceGeneratedContentGFV2(in.DstFolder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mlog.Printf(`gofmt go files in "%s"`, in.DstFolder)
|
||||
utils.GoFmt(in.DstFolder)
|
||||
}
|
||||
|
||||
// auto update main.go.
|
||||
if err = c.checkAndUpdateMain(in.SrcFolder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mlog.Print(`done!`)
|
||||
return
|
||||
}
|
||||
|
||||
func (c CGenService) replaceGeneratedServiceContentGFV2(in CGenServiceInput) (err error) {
|
||||
return gfile.ReplaceDirFunc(func(path, content string) string {
|
||||
if gstr.Contains(content, `"github.com/gogf/gf`) && !gstr.Contains(content, `"github.com/gogf/gf/v2`) {
|
||||
content = gstr.Replace(content, `"github.com/gogf/gf"`, `"github.com/gogf/gf/v2"`)
|
||||
content = gstr.Replace(content, `"github.com/gogf/gf/`, `"github.com/gogf/gf/v2/`)
|
||||
return content
|
||||
func (c CGenService) checkAndUpdateMain(srcFolder string) (err error) {
|
||||
var (
|
||||
logicPackageName = gstr.ToLower(gfile.Basename(srcFolder))
|
||||
logicFilePath = gfile.Join(srcFolder, logicPackageName+".go")
|
||||
importPath = utils.GetImportPath(logicFilePath)
|
||||
importStr = fmt.Sprintf(`_ "%s"`, importPath)
|
||||
mainFilePath = gfile.Join(gfile.Dir(gfile.Dir(gfile.Dir(logicFilePath))), "main.go")
|
||||
mainFileContent = gfile.GetContents(mainFilePath)
|
||||
)
|
||||
// No main content found.
|
||||
if mainFileContent == "" {
|
||||
return nil
|
||||
}
|
||||
if gstr.Contains(mainFileContent, importStr) {
|
||||
return nil
|
||||
}
|
||||
match, err := gregex.MatchString(`import \(([\s\S]+?)\)`, mainFileContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// No match.
|
||||
if len(match) < 2 {
|
||||
return nil
|
||||
}
|
||||
lines := garray.NewStrArrayFrom(gstr.Split(match[1], "\n"))
|
||||
for i, line := range lines.Slice() {
|
||||
line = gstr.Trim(line)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
return content
|
||||
}, in.DstFolder, "*.go", false)
|
||||
if line[0] == '_' {
|
||||
continue
|
||||
}
|
||||
// Insert the logic import into imports.
|
||||
if err = lines.InsertBefore(i, fmt.Sprintf("\t%s\n\n", importStr)); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
mainFileContent, err = gregex.ReplaceString(
|
||||
`import \(([\s\S]+?)\)`,
|
||||
fmt.Sprintf(`import (%s)`, lines.Join("\n")),
|
||||
mainFileContent,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mlog.Print(`update main.go`)
|
||||
err = gfile.PutContents(mainFilePath, mainFileContent)
|
||||
utils.GoFmt(mainFilePath)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
|
||||
@@ -9,29 +16,90 @@ import (
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
func (c CGenService) calculateImportedPackages(fileContent string, srcImportedPackages *garray.SortedStrArray) (err error) {
|
||||
type packageItem struct {
|
||||
Alias string
|
||||
Path string
|
||||
RawImport string
|
||||
}
|
||||
|
||||
func (c CGenService) calculateImportedPackages(fileContent string) (packages []packageItem, err error) {
|
||||
f, err := parser.ParseFile(token.NewFileSet(), "", fileContent, parser.ImportsOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
packages = make([]packageItem, 0)
|
||||
for _, s := range f.Imports {
|
||||
if s.Path != nil {
|
||||
if s.Name != nil {
|
||||
// If it has alias, and it is not `_`.
|
||||
if pkgAlias := s.Name.String(); pkgAlias != "_" {
|
||||
srcImportedPackages.Add(pkgAlias + " " + s.Path.Value)
|
||||
packages = append(packages, packageItem{
|
||||
Alias: pkgAlias,
|
||||
Path: s.Path.Value,
|
||||
RawImport: pkgAlias + " " + s.Path.Value,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// no alias
|
||||
srcImportedPackages.Add(s.Path.Value)
|
||||
packages = append(packages, packageItem{
|
||||
Alias: "",
|
||||
Path: s.Path.Value,
|
||||
RawImport: s.Path.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return packages, nil
|
||||
}
|
||||
|
||||
func (c CGenService) calculateCodeCommented(in CGenServiceInput, fileContent string, srcCodeCommentedMap map[string]string) error {
|
||||
matches, err := gregex.MatchAllString(`((((//.*)|(/\*[\s\S]*?\*/))\s)+)func \((.+?)\) ([\s\S]+?) {`, fileContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, match := range matches {
|
||||
var (
|
||||
structName string
|
||||
structMatch []string
|
||||
funcReceiver = gstr.Trim(match[1+5])
|
||||
receiverArray = gstr.SplitAndTrim(funcReceiver, " ")
|
||||
functionHead = gstr.Trim(gstr.Replace(match[2+5], "\n", ""))
|
||||
commentedInfo = ""
|
||||
)
|
||||
if len(receiverArray) > 1 {
|
||||
structName = receiverArray[1]
|
||||
} else if len(receiverArray) == 1 {
|
||||
structName = receiverArray[0]
|
||||
}
|
||||
structName = gstr.Trim(structName, "*")
|
||||
|
||||
// Case of:
|
||||
// Xxx(\n ctx context.Context, req *v1.XxxReq,\n) -> Xxx(ctx context.Context, req *v1.XxxReq)
|
||||
functionHead = gstr.Replace(functionHead, `,)`, `)`)
|
||||
functionHead, _ = gregex.ReplaceString(`\(\s+`, `(`, functionHead)
|
||||
functionHead, _ = gregex.ReplaceString(`\s{2,}`, ` `, functionHead)
|
||||
if !gstr.IsLetterUpper(functionHead[0]) {
|
||||
continue
|
||||
}
|
||||
// Match and pick the struct name from receiver.
|
||||
if structMatch, err = gregex.MatchString(in.StPattern, structName); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(structMatch) < 1 {
|
||||
continue
|
||||
}
|
||||
structName = gstr.CaseCamel(structMatch[1])
|
||||
|
||||
commentedInfo = match[1]
|
||||
if len(commentedInfo) > 0 {
|
||||
srcCodeCommentedMap[fmt.Sprintf("%s-%s", structName, functionHead)] = commentedInfo
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c CGenService) calculateInterfaceFunctions(
|
||||
in CGenServiceInput, fileContent string, srcPkgInterfaceMap map[string]*garray.StrArray, dstPackageName string,
|
||||
in CGenServiceInput, fileContent string, srcPkgInterfaceMap map[string]*garray.StrArray,
|
||||
) (err error) {
|
||||
var (
|
||||
ok bool
|
||||
@@ -53,7 +121,7 @@ func (c CGenService) calculateInterfaceFunctions(
|
||||
)
|
||||
if len(receiverArray) > 1 {
|
||||
structName = receiverArray[1]
|
||||
} else {
|
||||
} else if len(receiverArray) == 1 {
|
||||
structName = receiverArray[0]
|
||||
}
|
||||
structName = gstr.Trim(structName, "*")
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package genservice
|
||||
|
||||
import (
|
||||
@@ -21,6 +27,7 @@ type generateServiceFilesInput struct {
|
||||
SrcImportedPackages []string
|
||||
SrcPackageName string
|
||||
DstPackageName string
|
||||
SrcCodeCommentedMap map[string]string
|
||||
}
|
||||
|
||||
func (c CGenService) generateServiceFile(in generateServiceFilesInput) (ok bool, err error) {
|
||||
@@ -41,6 +48,13 @@ func (c CGenService) generateServiceFile(in generateServiceFilesInput) (ok bool,
|
||||
generatedContent += "\n"
|
||||
for structName, funcArray := range in.SrcStructFunctions {
|
||||
allFuncArray.Append(funcArray.Slice()...)
|
||||
// Add comments to a method.
|
||||
for index, funcName := range funcArray.Slice() {
|
||||
if commentedInfo, exist := in.SrcCodeCommentedMap[fmt.Sprintf("%s-%s", structName, funcName)]; exist {
|
||||
funcName = commentedInfo + funcName
|
||||
_ = funcArray.Set(index, funcName)
|
||||
}
|
||||
}
|
||||
generatedContent += gstr.Trim(gstr.ReplaceByMap(consts.TemplateGenServiceContentInterface, g.MapStrStr{
|
||||
"{InterfaceName}": "I" + structName,
|
||||
"{FuncDefinition}": funcArray.Join("\n\t"),
|
||||
@@ -56,7 +70,7 @@ func (c CGenService) generateServiceFile(in generateServiceFilesInput) (ok bool,
|
||||
generatingInterfaceCheck string
|
||||
)
|
||||
// Variable definitions.
|
||||
for structName, _ := range in.SrcStructFunctions {
|
||||
for structName := range in.SrcStructFunctions {
|
||||
generatingInterfaceCheck = fmt.Sprintf(`[^\w\d]+%s.I%s[^\w\d]`, in.DstPackageName, structName)
|
||||
if gregex.IsMatchString(generatingInterfaceCheck, generatedContent) {
|
||||
continue
|
||||
@@ -75,7 +89,7 @@ func (c CGenService) generateServiceFile(in generateServiceFilesInput) (ok bool,
|
||||
generatedContent += "\n"
|
||||
}
|
||||
// Variable register function definitions.
|
||||
for structName, _ := range in.SrcStructFunctions {
|
||||
for structName := range in.SrcStructFunctions {
|
||||
generatingInterfaceCheck = fmt.Sprintf(`[^\w\d]+%s.I%s[^\w\d]`, in.DstPackageName, structName)
|
||||
if gregex.IsMatchString(generatingInterfaceCheck, generatedContent) {
|
||||
continue
|
||||
@@ -114,6 +128,7 @@ func (c CGenService) generateServiceFile(in generateServiceFilesInput) (ok bool,
|
||||
// isToGenerateServiceGoFile checks and returns whether the service content dirty.
|
||||
func (c CGenService) isToGenerateServiceGoFile(dstPackageName, filePath string, funcArray *garray.StrArray) bool {
|
||||
var (
|
||||
err error
|
||||
fileContent = gfile.GetContents(filePath)
|
||||
generatedFuncArray = garray.NewSortedStrArrayFrom(funcArray.Slice())
|
||||
contentFuncArray = garray.NewSortedStrArray()
|
||||
@@ -121,6 +136,12 @@ func (c CGenService) isToGenerateServiceGoFile(dstPackageName, filePath string,
|
||||
if fileContent == "" {
|
||||
return true
|
||||
}
|
||||
// remove all comments.
|
||||
fileContent, err = gregex.ReplaceString(`/[/|\*](.*)`, "", fileContent)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return false
|
||||
}
|
||||
matches, _ := gregex.MatchAllString(`\s+interface\s+{([\s\S]+?)}`, fileContent)
|
||||
for _, match := range matches {
|
||||
contentFuncArray.Append(gstr.SplitAndTrim(match[1], "\n")...)
|
||||
@@ -145,29 +166,30 @@ func (c CGenService) isToGenerateServiceGoFile(dstPackageName, filePath string,
|
||||
return false
|
||||
}
|
||||
|
||||
// generateInitializationFile generates `logic.go`.
|
||||
func (c CGenService) generateInitializationFile(in CGenServiceInput, importSrcPackages []string) (err error) {
|
||||
var (
|
||||
srcPackageName = gstr.ToLower(gfile.Basename(in.SrcFolder))
|
||||
srcFilePath = gfile.Join(in.SrcFolder, srcPackageName+".go")
|
||||
srcImports string
|
||||
logicPackageName = gstr.ToLower(gfile.Basename(in.SrcFolder))
|
||||
logicFilePath = gfile.Join(in.SrcFolder, logicPackageName+".go")
|
||||
logicImports string
|
||||
generatedContent string
|
||||
)
|
||||
if !utils.IsFileDoNotEdit(srcFilePath) {
|
||||
mlog.Debugf(`ignore file as it is manually maintained: %s`, srcFilePath)
|
||||
if !utils.IsFileDoNotEdit(logicFilePath) {
|
||||
mlog.Debugf(`ignore file as it is manually maintained: %s`, logicFilePath)
|
||||
return nil
|
||||
}
|
||||
for _, importSrcPackage := range importSrcPackages {
|
||||
srcImports += fmt.Sprintf(`%s_ "%s"%s`, "\t", importSrcPackage, "\n")
|
||||
logicImports += fmt.Sprintf(`%s_ "%s"%s`, "\t", importSrcPackage, "\n")
|
||||
}
|
||||
generatedContent = gstr.ReplaceByMap(consts.TemplateGenServiceLogicContent, g.MapStrStr{
|
||||
"{PackageName}": srcPackageName,
|
||||
"{Imports}": srcImports,
|
||||
"{PackageName}": logicPackageName,
|
||||
"{Imports}": logicImports,
|
||||
})
|
||||
mlog.Printf(`generating init go file: %s`, srcFilePath)
|
||||
if err = gfile.PutContents(srcFilePath, generatedContent); err != nil {
|
||||
mlog.Printf(`generating init go file: %s`, logicFilePath)
|
||||
if err = gfile.PutContents(logicFilePath, generatedContent); err != nil {
|
||||
return err
|
||||
}
|
||||
utils.GoFmt(srcFilePath)
|
||||
utils.GoFmt(logicFilePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
30
server/internal/library/hggen/internal/cmd/testdata/fix25_content.go.txt
vendored
Normal file
30
server/internal/library/hggen/internal/cmd/testdata/fix25_content.go.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
)
|
||||
|
||||
func Test_Router_Hook_Multi(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/multi-hook", func(r *ghttp.Request) {
|
||||
r.Response.Write("show")
|
||||
})
|
||||
|
||||
s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/multi-hook/{id}", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.Response.Write("2")
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const (
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenCtrlControllerEmpty = `
|
||||
package {Module}
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlControllerNewEmpty = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package {Module}
|
||||
|
||||
import (
|
||||
{ImportPath}
|
||||
)
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlControllerNewFunc = `
|
||||
type {CtrlName} struct{}
|
||||
|
||||
func {NewFuncName}() {InterfaceName} {
|
||||
return &{CtrlName}{}
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlControllerMethodFunc = `
|
||||
package {Module}
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"{ImportPath}"
|
||||
)
|
||||
|
||||
func (c *{CtrlName}) {MethodName}(ctx context.Context, req *{Version}.{MethodName}Req) (res *{Version}.{MethodName}Res, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
`
|
||||
|
||||
const TemplateGenCtrlApiInterface = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package {Module}
|
||||
|
||||
import (
|
||||
{ImportPaths}
|
||||
)
|
||||
|
||||
{Interfaces}
|
||||
`
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenCtrlSdkPkgNew = `
|
||||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package {PkgName}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/contrib/sdk/httpclient/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type implementer struct {
|
||||
config httpclient.Config
|
||||
}
|
||||
|
||||
func New(config httpclient.Config) iClient {
|
||||
if !gstr.HasPrefix(config.URL, "http") {
|
||||
config.URL = fmt.Sprintf("http://%s", config.URL)
|
||||
}
|
||||
if config.Logger == nil {
|
||||
config.Logger = g.Log()
|
||||
}
|
||||
return &implementer{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlSdkIClient = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package {PkgName}
|
||||
|
||||
import (
|
||||
)
|
||||
|
||||
type iClient interface {
|
||||
}
|
||||
`
|
||||
|
||||
const TemplateGenCtrlSdkImplementer = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package {PkgName}
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/contrib/sdk/httpclient/v2"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
{ImportPaths}
|
||||
)
|
||||
|
||||
type implementer{ImplementerName} struct {
|
||||
*httpclient.Client
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlSdkImplementerNew = `
|
||||
func (i *implementer) {ImplementerName}() {Module}.I{ImplementerName} {
|
||||
var (
|
||||
client = httpclient.New(i.config)
|
||||
prefix = gstr.TrimRight(i.config.URL, "/") + "{VersionPrefix}"
|
||||
)
|
||||
client.Client = client.Prefix(prefix)
|
||||
return &implementer{ImplementerName}{client}
|
||||
}
|
||||
`
|
||||
|
||||
const TemplateGenCtrlSdkImplementerFunc = `
|
||||
func (i *implementer{ImplementerName}) {MethodName}(ctx context.Context, req *{Version}.{MethodName}Req) (res *{Version}.{MethodName}Res, err error) {
|
||||
err = i.Request(ctx, req, &res)
|
||||
return
|
||||
}
|
||||
`
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenDaoIndexContent = `
|
||||
@@ -33,7 +39,7 @@ var (
|
||||
|
||||
const TemplateGenDaoInternalContent = `
|
||||
// ==========================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr}
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr}
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenDaoDoContent = `
|
||||
// =================================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr}
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr}
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenDaoEntityContent = `
|
||||
// =================================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr}
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr}
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
@@ -8,7 +8,7 @@ package consts
|
||||
|
||||
const TemplateGenEnums = `
|
||||
// ================================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT.
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ================================================================================
|
||||
|
||||
package {PackageName}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplatePbEntityMessageContent = `
|
||||
// ==========================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT.
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package {PackageName};
|
||||
|
||||
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
|
||||
|
||||
option go_package = "{GoPackage}";
|
||||
{OptionContent}
|
||||
{Imports}
|
||||
|
||||
{EntityMessage}
|
||||
`
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenServiceContentHead = `
|
||||
// ================================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT.
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// You can delete these comments if you wish manually maintain this interface file.
|
||||
// ================================================================================
|
||||
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenServiceLogicContent = `
|
||||
// ==========================================================================
|
||||
// Code generated by GoFrame CLI tool. DO NOT EDIT.
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package {PackageName}
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package packed
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
@@ -29,6 +35,7 @@ type serviceInstallAvailablePath struct {
|
||||
filePath string
|
||||
writable bool
|
||||
installed bool
|
||||
IsSelf bool
|
||||
}
|
||||
|
||||
func (s serviceInstall) Run(ctx context.Context) (err error) {
|
||||
@@ -104,6 +111,8 @@ func (s serviceInstall) Run(ctx context.Context) (err error) {
|
||||
)
|
||||
if input != "" {
|
||||
inputID = gconv.Int(input)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
// Check if out of range.
|
||||
if inputID >= len(paths) || inputID < 0 {
|
||||
@@ -131,17 +140,17 @@ func (s serviceInstall) Run(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
// IsInstalled checks and returns whether the binary is installed.
|
||||
func (s serviceInstall) IsInstalled() bool {
|
||||
func (s serviceInstall) IsInstalled() (*serviceInstallAvailablePath, bool) {
|
||||
paths := s.getAvailablePaths()
|
||||
for _, aPath := range paths {
|
||||
if aPath.installed {
|
||||
return true
|
||||
return &aPath, true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// getGoPathBinFilePath retrieves ad returns the GOPATH/bin path for binary.
|
||||
// getGoPathBin retrieves ad returns the GOPATH/bin path for binary.
|
||||
func (s serviceInstall) getGoPathBin() string {
|
||||
if goPath := genv.Get(`GOPATH`).String(); goPath != "" {
|
||||
return gfile.Join(goPath, "bin")
|
||||
@@ -214,6 +223,7 @@ func (s serviceInstall) checkAndAppendToAvailablePath(folderPaths []serviceInsta
|
||||
filePath = gfile.Join(dirPath, binaryFileName)
|
||||
writable = gfile.IsWritable(dirPath)
|
||||
installed = gfile.Exists(filePath)
|
||||
self = gfile.SelfPath() == filePath
|
||||
)
|
||||
if !writable && !installed {
|
||||
return folderPaths
|
||||
@@ -225,5 +235,6 @@ func (s serviceInstall) checkAndAppendToAvailablePath(folderPaths []serviceInsta
|
||||
writable: writable,
|
||||
filePath: filePath,
|
||||
installed: installed,
|
||||
IsSelf: self,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package allyes
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package mlog
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"context"
|
||||
"fmt"
|
||||
"golang.org/x/tools/imports"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"hotgo/internal/library/hggen/internal/consts"
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
@@ -36,6 +46,13 @@ func GoFmt(path string) {
|
||||
}
|
||||
}
|
||||
|
||||
// GoModTidy executes `go mod tidy` at specified directory `dirPath`.
|
||||
func GoModTidy(ctx context.Context, dirPath string) error {
|
||||
command := fmt.Sprintf(`cd %s && go mod tidy`, dirPath)
|
||||
err := gproc.ShellRun(ctx, command)
|
||||
return err
|
||||
}
|
||||
|
||||
// IsFileDoNotEdit checks and returns whether file contains `do not edit` key.
|
||||
func IsFileDoNotEdit(filePath string) bool {
|
||||
if !gfile.Exists(filePath) {
|
||||
@@ -43,3 +60,78 @@ func IsFileDoNotEdit(filePath string) bool {
|
||||
}
|
||||
return gstr.Contains(gfile.GetContents(filePath), consts.DoNotEditKey)
|
||||
}
|
||||
|
||||
// ReplaceGeneratedContentGFV2 replaces generated go content from goframe v1 to v2.
|
||||
func ReplaceGeneratedContentGFV2(folderPath string) (err error) {
|
||||
return gfile.ReplaceDirFunc(func(path, content string) string {
|
||||
if gstr.Contains(content, `"github.com/gogf/gf`) && !gstr.Contains(content, `"github.com/gogf/gf/v2`) {
|
||||
content = gstr.Replace(content, `"github.com/gogf/gf"`, `"github.com/gogf/gf/v2"`)
|
||||
content = gstr.Replace(content, `"github.com/gogf/gf/`, `"github.com/gogf/gf/v2/`)
|
||||
content = gstr.Replace(content, `"github.com/gogf/gf/v2/contrib/`, `"github.com/gogf/gf/contrib/`)
|
||||
return content
|
||||
}
|
||||
return content
|
||||
}, folderPath, "*.go", true)
|
||||
}
|
||||
|
||||
// GetImportPath calculates and returns the golang import path for given `filePath`.
|
||||
// Note that it needs a `go.mod` in current working directory or parent directories to detect the path.
|
||||
func GetImportPath(filePath string) string {
|
||||
// If `filePath` does not exist, create it firstly to find the import path.
|
||||
var realPath = gfile.RealPath(filePath)
|
||||
if realPath == "" {
|
||||
_ = gfile.Mkdir(filePath)
|
||||
realPath = gfile.RealPath(filePath)
|
||||
}
|
||||
|
||||
var (
|
||||
newDir = gfile.Dir(realPath)
|
||||
oldDir string
|
||||
suffix string
|
||||
goModName = "go.mod"
|
||||
goModPath string
|
||||
importPath string
|
||||
)
|
||||
|
||||
if gfile.IsDir(filePath) {
|
||||
suffix = gfile.Basename(filePath)
|
||||
}
|
||||
for {
|
||||
goModPath = gfile.Join(newDir, goModName)
|
||||
if gfile.Exists(goModPath) {
|
||||
match, _ := gregex.MatchString(`^module\s+(.+)\s*`, gfile.GetContents(goModPath))
|
||||
importPath = gstr.Trim(match[1]) + "/" + suffix
|
||||
importPath = gstr.Replace(importPath, `\`, `/`)
|
||||
importPath = gstr.TrimRight(importPath, `/`)
|
||||
return importPath
|
||||
}
|
||||
oldDir = newDir
|
||||
newDir = gfile.Dir(oldDir)
|
||||
if newDir == oldDir {
|
||||
return ""
|
||||
}
|
||||
suffix = gfile.Basename(oldDir) + "/" + suffix
|
||||
}
|
||||
}
|
||||
|
||||
// GetModPath retrieves and returns the file path of go.mod for current project.
|
||||
func GetModPath() string {
|
||||
var (
|
||||
oldDir = gfile.Pwd()
|
||||
newDir = gfile.Dir(oldDir)
|
||||
goModName = "go.mod"
|
||||
goModPath string
|
||||
)
|
||||
for {
|
||||
goModPath = gfile.Join(newDir, goModName)
|
||||
if gfile.Exists(goModPath) {
|
||||
return goModPath
|
||||
}
|
||||
oldDir = newDir
|
||||
newDir = gfile.Dir(oldDir)
|
||||
if newDir == oldDir {
|
||||
break
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/utility/mlog"
|
||||
)
|
||||
|
||||
// HTTPDownloadFileWithPercent downloads target url file to local path with percent process printing.
|
||||
func HTTPDownloadFileWithPercent(url string, localSaveFilePath string) error {
|
||||
start := time.Now()
|
||||
out, err := os.Create(localSaveFilePath)
|
||||
if err != nil {
|
||||
return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath)
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
headResp, err := http.Head(url)
|
||||
if err != nil {
|
||||
return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath)
|
||||
}
|
||||
defer headResp.Body.Close()
|
||||
|
||||
size, err := strconv.Atoi(headResp.Header.Get("Content-Length"))
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "retrieve Content-Length failed")
|
||||
}
|
||||
doneCh := make(chan int64)
|
||||
|
||||
go doPrintDownloadPercent(doneCh, localSaveFilePath, int64(size))
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
wroteBytesCount, err := io.Copy(out, resp.Body)
|
||||
if err != nil {
|
||||
return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath)
|
||||
}
|
||||
|
||||
doneCh <- wroteBytesCount
|
||||
elapsed := time.Since(start)
|
||||
if elapsed > time.Minute {
|
||||
mlog.Printf(`download completed in %.0fm`, float64(elapsed)/float64(time.Minute))
|
||||
} else {
|
||||
mlog.Printf(`download completed in %.0fs`, elapsed.Seconds())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func doPrintDownloadPercent(doneCh chan int64, localSaveFilePath string, total int64) {
|
||||
var (
|
||||
stop = false
|
||||
lastPercentFmt string
|
||||
)
|
||||
for {
|
||||
select {
|
||||
case <-doneCh:
|
||||
stop = true
|
||||
|
||||
default:
|
||||
file, err := os.Open(localSaveFilePath)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
fi, err := file.Stat()
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
size := fi.Size()
|
||||
if size == 0 {
|
||||
size = 1
|
||||
}
|
||||
var (
|
||||
percent = float64(size) / float64(total) * 100
|
||||
percentFmt = fmt.Sprintf(`%.0f`, percent) + "%"
|
||||
)
|
||||
if lastPercentFmt != percentFmt {
|
||||
lastPercentFmt = percentFmt
|
||||
mlog.Print(percentFmt)
|
||||
}
|
||||
}
|
||||
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package utils_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"hotgo/internal/library/hggen/internal/utility/utils"
|
||||
)
|
||||
|
||||
func Test_GetModPath(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
goModPath := utils.GetModPath()
|
||||
fmt.Println(goModPath)
|
||||
})
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
//
|
||||
package views
|
||||
|
||||
import (
|
||||
@@ -20,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
// DoTableColumns 获取指定表生成字段列表
|
||||
func DoTableColumns(ctx context.Context, in sysin.GenCodesColumnListInp, config gendao.CGenDaoInput) (fields []*sysin.GenCodesColumnListModel, err error) {
|
||||
func DoTableColumns(ctx context.Context, in *sysin.GenCodesColumnListInp, config gendao.CGenDaoInput) (fields []*sysin.GenCodesColumnListModel, err error) {
|
||||
var (
|
||||
sql = "select ORDINAL_POSITION as `id`, COLUMN_NAME as `name`, COLUMN_COMMENT as `dc`, DATA_TYPE as `dataType`, COLUMN_TYPE as `sqlType`, CHARACTER_MAXIMUM_LENGTH as `length`, IS_NULLABLE as `isAllowNull`, COLUMN_DEFAULT as `defaultValue`, COLUMN_KEY as `index`, EXTRA as `extra` from information_schema.COLUMNS where TABLE_SCHEMA = '%s' and TABLE_NAME = '%s' ORDER BY `id` ASC"
|
||||
conf = g.DB(in.Name).GetConfig()
|
||||
@@ -42,7 +41,6 @@ func DoTableColumns(ctx context.Context, in sysin.GenCodesColumnListInp, config
|
||||
CustomAttributes(ctx, field, config)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -65,10 +63,7 @@ func CustomAttributes(ctx context.Context, field *sysin.GenCodesColumnListModel,
|
||||
|
||||
// GenGotype 生成字段的go类型
|
||||
func GenGotype(ctx context.Context, field *sysin.GenCodesColumnListModel, in gendao.CGenDaoInput) (goName, typeName, tsName, tsType string) {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
|
||||
var err error
|
||||
tsName = getJsonTagFromCase(field.Name, in.JsonCase)
|
||||
goName = gstr.CaseCamel(field.Name)
|
||||
|
||||
@@ -100,7 +95,6 @@ func GenGotype(ctx context.Context, field *sysin.GenCodesColumnListModel, in gen
|
||||
}
|
||||
|
||||
tsType = ShiftMap[typeName]
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -317,5 +317,10 @@ func setDefaultValue(field *sysin.GenCodesColumnListModel) {
|
||||
value = consts.ConvType(field.DefaultValue, field.GoType)
|
||||
}
|
||||
|
||||
// 时间类型不做默认值处理
|
||||
if field.GoType == GoTypeGTime {
|
||||
value = ""
|
||||
}
|
||||
|
||||
field.DefaultValue = value
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ package views
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
@@ -67,14 +66,14 @@ type CurdOptions struct {
|
||||
HeadOps []string `json:"headOps"`
|
||||
Join []*CurdOptionsJoin `json:"join"`
|
||||
Menu *CurdOptionsMenu `json:"menu"`
|
||||
Step *CurdStep // 转换后的流程控制条件
|
||||
dictMap g.Map // 字典选项 -> 字段映射关系
|
||||
TemplateGroup string `json:"templateGroup"`
|
||||
ApiPrefix string `json:"apiPrefix"`
|
||||
Step *CurdStep // 转换后的流程控制条件
|
||||
dictMap g.Map // 字典选项 -> 字段映射关系
|
||||
}
|
||||
|
||||
type CurdPreviewInput struct {
|
||||
In sysin.GenCodesPreviewInp // 提交参数
|
||||
In *sysin.GenCodesPreviewInp // 提交参数
|
||||
DaoConfig gendao.CGenDaoInput // 生成dao配置
|
||||
Config *model.GenerateConfig // 生成配置
|
||||
view *gview.View // 视图模板
|
||||
@@ -102,7 +101,7 @@ func (l *gCurd) initInput(ctx context.Context, in *CurdPreviewInput) (err error)
|
||||
}
|
||||
|
||||
if len(in.masterFields) == 0 {
|
||||
if in.masterFields, err = DoTableColumns(ctx, sysin.GenCodesColumnListInp{Name: in.In.DbName, Table: in.In.TableName}, in.DaoConfig); err != nil {
|
||||
if in.masterFields, err = DoTableColumns(ctx, &sysin.GenCodesColumnListInp{Name: in.In.DbName, Table: in.In.TableName}, in.DaoConfig); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -236,7 +235,7 @@ func (l *gCurd) DoBuild(ctx context.Context, in *CurdBuildInput) (err error) {
|
||||
for name, f := range in.BeforeEvent {
|
||||
if gstr.InArray(in.PreviewIn.options.AutoOps, name) {
|
||||
if err = f(ctx); err != nil {
|
||||
return fmt.Errorf("in doBuild operation beforeEvent to '%s' failed::%v", name, err)
|
||||
return gerror.Newf("in doBuild operation beforeEvent to '%s' failed:%v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,7 +253,7 @@ func (l *gCurd) DoBuild(ctx context.Context, in *CurdBuildInput) (err error) {
|
||||
}
|
||||
|
||||
if err = gfile.PutContents(vi.Path, strings.TrimSpace(vi.Content)); err != nil {
|
||||
return fmt.Errorf("writing content to '%s' failed: %v", vi.Path, err)
|
||||
return gerror.Newf("writing content to '%s' failed: %v", vi.Path, err)
|
||||
}
|
||||
|
||||
if gstr.Str(vi.Path, `.`) == ".sql" && needExecSql {
|
||||
@@ -266,7 +265,6 @@ func (l *gCurd) DoBuild(ctx context.Context, in *CurdBuildInput) (err error) {
|
||||
if gstr.Str(vi.Path, `.`) == ".go" {
|
||||
utils.GoFmt(vi.Path)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 后置操作
|
||||
@@ -274,7 +272,7 @@ func (l *gCurd) DoBuild(ctx context.Context, in *CurdBuildInput) (err error) {
|
||||
for name, f := range in.AfterEvent {
|
||||
if gstr.InArray(in.PreviewIn.options.AutoOps, name) {
|
||||
if err = f(ctx); err != nil {
|
||||
return fmt.Errorf("in doBuild operation afterEvent to '%s' failed::%v", name, err)
|
||||
return gerror.Newf("in doBuild operation afterEvent to '%s' failed:%v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,12 +513,12 @@ func (l *gCurd) generateWebModelContent(ctx context.Context, in *CurdPreviewInpu
|
||||
|
||||
tplData, err := l.webModelTplData(ctx, in)
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
genFile.Content, err = in.view.Parse(ctx, name+".template", tplData)
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].WebViewsPath, gstr.LcFirst(in.In.VarName), "model.ts")
|
||||
@@ -564,7 +562,6 @@ func (l *gCurd) generateWebIndexContent(ctx context.Context, in *CurdPreviewInpu
|
||||
genFile.Meth = consts.GenCodesBuildMethCover
|
||||
}
|
||||
in.content.Views[name] = genFile
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,6 @@ func (l *gCurd) generateStructFieldDefinition(field *sysin.GenCodesColumnListMod
|
||||
default:
|
||||
panic("inputType is invalid")
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ func (l *gCurd) generateWebEditFormItem(ctx context.Context, in *CurdPreviewInpu
|
||||
component = fmt.Sprintf("<n-form-item label=\"%s\" path=\"%s\">\n <n-input type=\"textarea\" placeholder=\"%s\" v-model:value=\"params.%s\" />\n </n-form-item>", field.Dc, field.TsName, field.Dc, field.TsName)
|
||||
|
||||
case FormModeInputEditor:
|
||||
component = fmt.Sprintf("<n-form-item label=\"%s\" path=\"%s\">\n <Editor style=\"height: 450px\" v-model:value=\"params.%s\" />\n </n-form-item>", field.Dc, field.TsName, field.TsName)
|
||||
component = fmt.Sprintf("<n-form-item label=\"%s\" path=\"%s\">\n <Editor style=\"height: 450px\" id=\"%s\" v-model:value=\"params.%s\" />\n </n-form-item>", field.Dc, field.TsName, field.TsName, field.TsName)
|
||||
|
||||
case FormModeInputDynamic:
|
||||
component = fmt.Sprintf("<n-form-item label=\"%s\" path=\"%s\">\n <n-dynamic-input\n v-model:value=\"params.%s\"\n preset=\"pair\"\n key-placeholder=\"键名\"\n value-placeholder=\"键值\"\n />\n </n-form-item>", field.Dc, field.TsName, field.TsName)
|
||||
|
||||
@@ -83,6 +83,5 @@ func (l *gCurd) webIndexTplData(ctx context.Context, in *CurdPreviewInput) (g.Ma
|
||||
}
|
||||
}
|
||||
data["isSearchForm"] = isSearchForm
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ func (l *gCurd) parseServFunName(templateGroup, varName string) string {
|
||||
if gstr.HasPrefix(varName, templateGroup) && varName != templateGroup {
|
||||
return varName
|
||||
}
|
||||
|
||||
return templateGroup + varName
|
||||
}
|
||||
|
||||
@@ -94,7 +93,6 @@ func ImportSql(ctx context.Context, path string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -144,7 +142,6 @@ func checkCurdPath(temp *model.GenerateAppCrudTemplate, addonName string) (err e
|
||||
if !gfile.Exists(temp.WebViewsPath) {
|
||||
return gerror.Newf(tip, "WebViewsPath", temp.WebViewsPath)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user