From b6221752cacf71aebfe44a389ad31345a1c69274 Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Sun, 8 Oct 2023 12:16:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20xlsx=20=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=AF=BC=E5=87=BA=E5=B7=A5=E5=85=B7=E5=8F=8A=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=EF=BC=8C=E5=8F=AF=E6=89=8B=E5=8A=A8=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E5=90=8E=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 3 + go.sum | 8 ++ planner/pce/exporter/cmd/exportgo.go | 107 ++++++++++++++++++++++ planner/pce/exporter/cmd/exportjson.go | 114 ++++++++++++++++++++++++ planner/pce/exporter/cmd/root.go | 23 +++++ planner/pce/exporter/main.go | 7 ++ planner/pce/exporter/xlsx_template.xlsx | Bin 0 -> 14974 bytes utils/str/str.go | 12 +++ 8 files changed, 274 insertions(+) create mode 100644 planner/pce/exporter/cmd/exportgo.go create mode 100644 planner/pce/exporter/cmd/exportjson.go create mode 100644 planner/pce/exporter/cmd/root.go create mode 100644 planner/pce/exporter/main.go create mode 100644 planner/pce/exporter/xlsx_template.xlsx diff --git a/go.mod b/go.mod index 3be8336..36c07ee 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/goccy/go-json v0.10.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jonboulle/clockwork v0.3.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/klauspost/compress v1.16.5 // indirect @@ -53,6 +54,8 @@ require ( github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/smarty/assertions v1.15.0 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/templexxx/cpu v0.1.0 // indirect github.com/templexxx/xorsimd v0.4.2 // indirect github.com/tidwall/match v1.1.1 // indirect diff --git a/go.sum b/go.sum index 46fe87a..239ad43 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,7 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhD github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -75,6 +76,8 @@ github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25d github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -145,12 +148,17 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= github.com/sony/sonyflake v1.2.0 h1:Pfr3A+ejSg+0SPqpoAmQgEtNDAhc2G1SUYk205qVMLQ= github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/planner/pce/exporter/cmd/exportgo.go b/planner/pce/exporter/cmd/exportgo.go new file mode 100644 index 0000000..fde1993 --- /dev/null +++ b/planner/pce/exporter/cmd/exportgo.go @@ -0,0 +1,107 @@ +package cmd + +import ( + "errors" + "github.com/kercylan98/minotaur/planner/pce" + "github.com/kercylan98/minotaur/planner/pce/cs" + "github.com/kercylan98/minotaur/planner/pce/tmpls" + "github.com/kercylan98/minotaur/utils/file" + "github.com/kercylan98/minotaur/utils/hash" + "github.com/kercylan98/minotaur/utils/str" + "github.com/spf13/cobra" + "github.com/tealeg/xlsx" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func init() { + var filePath, outPath, exclude string + + exportGo := &cobra.Command{ + Use: "go", + Short: "Export go language configuration code | 导出 go 语言配置代码", + RunE: func(cmd *cobra.Command, args []string) error { + + isDir, err := file.IsDir(outPath) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + isDir = filepath.Ext(outPath) == "" + } else { + return err + } + } + if isDir { + _ = os.MkdirAll(outPath, os.ModePerm) + outPath = filepath.Join(outPath, "config.go") + } else { + _ = os.MkdirAll(filepath.Dir(outPath), os.ModePerm) + } + + fpd, err := file.IsDir(filePath) + if err != nil { + return err + } + + var xlsxFiles []string + if fpd { + files, err := os.ReadDir(filePath) + if err != nil { + return err + } + for _, f := range files { + if f.IsDir() || !strings.HasSuffix(f.Name(), ".xlsx") || strings.HasPrefix(f.Name(), "~") { + continue + } + xlsxFiles = append(xlsxFiles, filepath.Join(filePath, f.Name())) + } + } else { + xlsxFiles = append(xlsxFiles, filePath) + } + + var golang []*pce.TmplStruct + var exporter = pce.NewExporter() + loader := pce.NewLoader(pce.GetFields()) + + excludes := hash.ToMapBool(str.SplitTrimSpace(exclude, ",")) + for _, xlsxFile := range xlsxFiles { + xf, err := xlsx.OpenFile(xlsxFile) + if err != nil { + return err + } + + for _, sheet := range xf.Sheets { + cx := cs.NewXlsx(sheet, cs.XlsxExportTypeServer) + if strings.HasPrefix(cx.GetDisplayName(), "#") || strings.HasPrefix(cx.GetConfigName(), "#") || excludes[cx.GetConfigName()] || excludes[cx.GetDisplayName()] { + continue + } + golang = append(golang, loader.LoadStruct(cx)) + } + } + + if raw, err := exporter.ExportStruct(tmpls.NewGolang(filepath.Base(filepath.Dir(outPath))), golang...); err != nil { + return err + } else { + if err := file.WriterFile(outPath, raw); err != nil { + return err + } + } + + _ = exec.Command("gofmt", "-w", outPath).Run() + return nil + }, + } + + exportGo.Flags().StringVarP(&filePath, "xlsx", "f", "", "xlsx file path or directory path | xlsx 文件路径或所在目录路径") + exportGo.Flags().StringVarP(&outPath, "output", "o", "", "output path | 输出的 go 文件路径") + exportGo.Flags().StringVarP(&exclude, "exclude", "e", "", "excluded configuration names or display names (comma separated) | 排除的配置名或显示名(英文逗号分隔)") + if err := exportGo.MarkFlagRequired("xlsx"); err != nil { + panic(err) + } + if err := exportGo.MarkFlagRequired("output"); err != nil { + panic(err) + } + + rootCmd.AddCommand(exportGo) +} diff --git a/planner/pce/exporter/cmd/exportjson.go b/planner/pce/exporter/cmd/exportjson.go new file mode 100644 index 0000000..718a87b --- /dev/null +++ b/planner/pce/exporter/cmd/exportjson.go @@ -0,0 +1,114 @@ +package cmd + +import ( + "errors" + "fmt" + "github.com/kercylan98/minotaur/planner/pce" + "github.com/kercylan98/minotaur/planner/pce/cs" + "github.com/kercylan98/minotaur/planner/pce/tmpls" + "github.com/kercylan98/minotaur/utils/file" + "github.com/kercylan98/minotaur/utils/hash" + "github.com/kercylan98/minotaur/utils/str" + "github.com/spf13/cobra" + "github.com/tealeg/xlsx" + "os" + "path/filepath" + "strings" +) + +func init() { + var filePath, outPath, exclude, exportType, prefix string + + exportJson := &cobra.Command{ + Use: "json", + Short: "Export json configuration data | 导出 json 配置数据", + RunE: func(cmd *cobra.Command, args []string) error { + + isDir, err := file.IsDir(outPath) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + isDir = filepath.Ext(outPath) == "" + } else { + return err + } + } + if !isDir { + return errors.New("output must be a directory path") + } + _ = os.MkdirAll(outPath, os.ModePerm) + + fpd, err := file.IsDir(filePath) + if err != nil { + return err + } + + var xlsxFiles []string + if fpd { + files, err := os.ReadDir(filePath) + if err != nil { + return err + } + for _, f := range files { + if f.IsDir() || !strings.HasSuffix(f.Name(), ".xlsx") || strings.HasPrefix(f.Name(), "~") { + continue + } + xlsxFiles = append(xlsxFiles, filepath.Join(filePath, f.Name())) + } + } else { + xlsxFiles = append(xlsxFiles, filePath) + } + + var exporter = pce.NewExporter() + loader := pce.NewLoader(pce.GetFields()) + + excludes := hash.ToMapBool(str.SplitTrimSpace(exclude, ",")) + for _, xlsxFile := range xlsxFiles { + xf, err := xlsx.OpenFile(xlsxFile) + if err != nil { + return err + } + + for _, sheet := range xf.Sheets { + var cx *cs.Xlsx + switch strings.TrimSpace(strings.ToLower(exportType)) { + case "c": + cx = cs.NewXlsx(sheet, cs.XlsxExportTypeClient) + case "s": + cx = cs.NewXlsx(sheet, cs.XlsxExportTypeServer) + } + if strings.HasPrefix(cx.GetDisplayName(), "#") || strings.HasPrefix(cx.GetConfigName(), "#") || excludes[cx.GetConfigName()] || excludes[cx.GetDisplayName()] { + continue + } + + if raw, err := exporter.ExportData(tmpls.NewJSON(), loader.LoadData(cx)); err != nil { + return err + } else { + jsonPath := filepath.Join(outPath, fmt.Sprintf("%s.%s.json", prefix, cx.GetConfigName())) + if err := file.WriterFile(jsonPath, raw); err != nil { + return err + } + } + } + } + + return nil + }, + } + + exportJson.Flags().StringVarP(&filePath, "xlsx", "f", "", "xlsx file path or directory path | xlsx 文件路径或所在目录路径") + exportJson.Flags().StringVarP(&outPath, "output", "o", "", "directory path of the output json file | 输出的 json 文件所在目录路径") + exportJson.Flags().StringVarP(&exportType, "type", "t", "", "export server configuration[s] or client configuration[c] | 导出服务端配置[s]还是客户端配置[c]") + exportJson.Flags().StringVarP(&prefix, "prefix", "p", "", "export configuration file name prefix | 导出配置文件名前缀") + exportJson.Flags().StringVarP(&exclude, "exclude", "e", "", "excluded configuration names or display names (comma separated) | 排除的配置名或显示名(英文逗号分隔)") + if err := exportJson.MarkFlagRequired("xlsx"); err != nil { + panic(err) + } + if err := exportJson.MarkFlagRequired("output"); err != nil { + panic(err) + } + if err := exportJson.MarkFlagRequired("type"); err != nil { + panic(err) + } + + rootCmd.AddCommand(exportJson) +} diff --git a/planner/pce/exporter/cmd/root.go b/planner/pce/exporter/cmd/root.go new file mode 100644 index 0000000..5d3ec53 --- /dev/null +++ b/planner/pce/exporter/cmd/root.go @@ -0,0 +1,23 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "os" +) + +// rootCmd 在没有任何子命令的情况下调用时的基本命令 +var rootCmd = &cobra.Command{ + Use: "exporter", + Short: "An exporter suitable for exporting xlsx configuration templates into go language configuration code and json data files. | 一个适合将 xlsx 配置模板导出为 go 语言配置代码和 json 数据文件的导出器。", + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, +} + +// Execute 将所有子命令添加到根命令并适当设置标志。这是由 main.main() 调用的。 rootCmd 只需要发生一次 +func Execute() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} diff --git a/planner/pce/exporter/main.go b/planner/pce/exporter/main.go new file mode 100644 index 0000000..0576ab7 --- /dev/null +++ b/planner/pce/exporter/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/kercylan98/minotaur/planner/pce/exporter/cmd" + +func main() { + cmd.Execute() +} diff --git a/planner/pce/exporter/xlsx_template.xlsx b/planner/pce/exporter/xlsx_template.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..7ebc65ea2eb7e93285b54963444ac502df6ba001 GIT binary patch literal 14974 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 literal 0 HcmV?d00001 diff --git a/utils/str/str.go b/utils/str/str.go index 9dc2fa2..1f40bc0 100644 --- a/utils/str/str.go +++ b/utils/str/str.go @@ -1,5 +1,7 @@ package str +import "strings" + const ( None = "" // 空字符串 Dunno = "?" // 未知 @@ -16,6 +18,16 @@ var ( SlashBytes = []byte("/") // 斜杠 ) +// SplitTrimSpace 按照空格分割字符串并去除空格 +func SplitTrimSpace(str, sep string) []string { + var strList = strings.Split(str, sep) + var result = make([]string, 0, len(strList)) + for _, s := range strList { + result = append(result, strings.TrimSpace(s)) + } + return result +} + // FirstUpper 首字母大写 func FirstUpper(str string) string { var upperStr string