feat: 重构 config 和 configexport 包
- 配置加载包 config 更名为 configuration - 配置导出包 configexport 更名为 pce - 重构 config 包加载方式,采用加载器的方式,并且支持多加载器 - 重构 configexport 包,支持通过实现模板的方式导出不同格式的数据文件及结构文件
This commit is contained in:
parent
8e2b4ebc89
commit
7e7a504421
|
@ -1,89 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
jsonIter "github.com/json-iterator/go"
|
|
||||||
"github.com/kercylan98/minotaur/utils/log"
|
|
||||||
"github.com/kercylan98/minotaur/utils/timer"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LoadHandle 配置加载处理函数
|
|
||||||
type LoadHandle func(handle func(filename string, config any) error)
|
|
||||||
|
|
||||||
// RefreshHandle 配置刷新处理函数
|
|
||||||
type RefreshHandle func()
|
|
||||||
|
|
||||||
const (
|
|
||||||
tickerLoadRefresh = "_tickerLoadRefresh"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
cLoadDir string
|
|
||||||
cTicker *timer.Ticker
|
|
||||||
cInterval time.Duration
|
|
||||||
cLoadHandle LoadHandle
|
|
||||||
cRefreshHandle RefreshHandle
|
|
||||||
json = jsonIter.ConfigCompatibleWithStandardLibrary
|
|
||||||
mutex sync.Mutex
|
|
||||||
isInit = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// Init 配置初始化
|
|
||||||
func Init(loadDir string, loadHandle LoadHandle, refreshHandle RefreshHandle) {
|
|
||||||
cLoadDir = loadDir
|
|
||||||
cLoadHandle = loadHandle
|
|
||||||
cRefreshHandle = refreshHandle
|
|
||||||
Load()
|
|
||||||
Refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load 加载配置
|
|
||||||
// - 加载后并不会刷新线上配置,需要执行 Refresh 函数对线上配置进行刷新
|
|
||||||
func Load() {
|
|
||||||
mutex.Lock()
|
|
||||||
cLoadHandle(func(filename string, config any) error {
|
|
||||||
bytes, err := os.ReadFile(filepath.Join(cLoadDir, filename))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = json.Unmarshal(bytes, &config); err == nil && isInit {
|
|
||||||
log.Info("Config", log.String("Name", filename), log.Bool("LoadSuccess", true))
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
isInit = false
|
|
||||||
mutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTickerLoad 通过定时器加载配置
|
|
||||||
// - 通过定时器加载配置后,会自动刷新线上配置
|
|
||||||
// - 调用该函数后将会立即加载并刷新一次配置,随后每隔 interval 时间加载并刷新一次配置
|
|
||||||
func WithTickerLoad(ticker *timer.Ticker, interval time.Duration) {
|
|
||||||
if ticker != cTicker && cTicker != nil {
|
|
||||||
cTicker.StopTimer(tickerLoadRefresh)
|
|
||||||
}
|
|
||||||
cTicker = ticker
|
|
||||||
cInterval = interval
|
|
||||||
cTicker.Loop(tickerLoadRefresh, timer.Instantly, cInterval, timer.Forever, func() {
|
|
||||||
Load()
|
|
||||||
Refresh()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// StopTickerLoad 停止通过定时器加载配置
|
|
||||||
func StopTickerLoad() {
|
|
||||||
if cTicker != nil {
|
|
||||||
cTicker.StopTimer(tickerLoadRefresh)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh 刷新配置
|
|
||||||
func Refresh() {
|
|
||||||
mutex.Lock()
|
|
||||||
cRefreshHandle()
|
|
||||||
OnConfigRefreshEvent()
|
|
||||||
mutex.Unlock()
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
// Package config 基于配置导表功能实现的配置加载及刷新功能
|
|
||||||
package config
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/log"
|
||||||
|
"github.com/kercylan98/minotaur/utils/timer"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tickerLoadRefresh = "_tickerLoadRefresh"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cTicker *timer.Ticker
|
||||||
|
cInterval time.Duration
|
||||||
|
cLoader []Loader
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init 配置初始化
|
||||||
|
// - 在初始化后会立即加载配置
|
||||||
|
func Init(loader ...Loader) {
|
||||||
|
cLoader = loader
|
||||||
|
Load()
|
||||||
|
Refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load 加载配置
|
||||||
|
// - 加载后并不会刷新线上配置,需要执行 Refresh 函数对线上配置进行刷新
|
||||||
|
func Load() {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
log.Error("Config", log.String("Action", "Load"), log.Err(err.(error)))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for _, loader := range cLoader {
|
||||||
|
loader.Load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh 刷新配置
|
||||||
|
func Refresh() {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
log.Error("Config", log.String("Action", "Refresh"), log.Err(err.(error)))
|
||||||
|
}
|
||||||
|
OnConfigRefreshEvent()
|
||||||
|
}()
|
||||||
|
for _, loader := range cLoader {
|
||||||
|
loader.Refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTickerLoad 通过定时器加载配置
|
||||||
|
// - 通过定时器加载配置后,会自动刷新线上配置
|
||||||
|
// - 调用该函数后不会立即刷新,而是在 interval 后加载并刷新一次配置,之后每隔 interval 加载并刷新一次配置
|
||||||
|
func WithTickerLoad(ticker *timer.Ticker, interval time.Duration) {
|
||||||
|
if ticker != cTicker && cTicker != nil {
|
||||||
|
cTicker.StopTimer(tickerLoadRefresh)
|
||||||
|
}
|
||||||
|
cTicker = ticker
|
||||||
|
cInterval = interval
|
||||||
|
cTicker.Loop(tickerLoadRefresh, cInterval, cInterval, timer.Forever, func() {
|
||||||
|
Load()
|
||||||
|
Refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopTickerLoad 停止通过定时器加载配置
|
||||||
|
func StopTickerLoad() {
|
||||||
|
if cTicker != nil {
|
||||||
|
cTicker.StopTimer(tickerLoadRefresh)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Package configuration 基于配置导表功能实现的配置加载及刷新功能
|
||||||
|
package configuration
|
|
@ -1,4 +1,4 @@
|
||||||
package config
|
package configuration
|
||||||
|
|
||||||
// RefreshEventHandle 配置刷新事件处理函数
|
// RefreshEventHandle 配置刷新事件处理函数
|
||||||
type RefreshEventHandle func()
|
type RefreshEventHandle func()
|
|
@ -0,0 +1,10 @@
|
||||||
|
package configuration
|
||||||
|
|
||||||
|
// Loader 配置加载器
|
||||||
|
type Loader interface {
|
||||||
|
// Load 加载配置
|
||||||
|
// - 加载后并不会刷新线上配置,需要执行 Refresh 函数对线上配置进行刷新
|
||||||
|
Load()
|
||||||
|
// Refresh 刷新线上配置
|
||||||
|
Refresh()
|
||||||
|
}
|
|
@ -1,213 +0,0 @@
|
||||||
package configexport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"github.com/kercylan98/minotaur/planner/configexport/internal"
|
|
||||||
"github.com/kercylan98/minotaur/utils/file"
|
|
||||||
"github.com/kercylan98/minotaur/utils/log"
|
|
||||||
"github.com/kercylan98/minotaur/utils/str"
|
|
||||||
"github.com/tealeg/xlsx"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime/debug"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
// New 创建一个导表配置
|
|
||||||
func New(xlsxPath string) *ConfigExport {
|
|
||||||
ce := &ConfigExport{xlsxPath: xlsxPath, exist: make(map[string]bool)}
|
|
||||||
xlsxFile, err := xlsx.OpenFile(xlsxPath)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for i := 0; i < len(xlsxFile.Sheets); i++ {
|
|
||||||
sheet := xlsxFile.Sheets[i]
|
|
||||||
if config, err := internal.NewConfig(sheet, ce.exist); err != nil {
|
|
||||||
switch err {
|
|
||||||
case internal.ErrReadConfigFailedSame:
|
|
||||||
log.Warn("ConfigExport",
|
|
||||||
log.String("File", xlsxPath),
|
|
||||||
log.String("Sheet", sheet.Name),
|
|
||||||
log.String("Info", "A configuration with the same name exists, skipped"),
|
|
||||||
)
|
|
||||||
case internal.ErrReadConfigFailedIgnore:
|
|
||||||
log.Info("ConfigExport",
|
|
||||||
log.String("File", xlsxPath),
|
|
||||||
log.String("Sheet", sheet.Name),
|
|
||||||
log.String("Info", "Excluded non-configuration table files"),
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
log.Error("ConfigExport",
|
|
||||||
log.String("File", xlsxPath),
|
|
||||||
log.String("Sheet", sheet.Name),
|
|
||||||
log.String("Info", "Excluded non-configuration table files"),
|
|
||||||
)
|
|
||||||
debug.PrintStack()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if config == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ce.configs = append(ce.configs, config)
|
|
||||||
ce.exist[config.Name] = true
|
|
||||||
|
|
||||||
log.Info("ConfigExport",
|
|
||||||
log.String("File", xlsxPath),
|
|
||||||
log.String("Sheet", sheet.Name),
|
|
||||||
log.String("Info", "Export successfully"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ce
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigExport struct {
|
|
||||||
xlsxPath string
|
|
||||||
configs []*internal.Config
|
|
||||||
exist map[string]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge 合并多个导表配置
|
|
||||||
func Merge(exports ...*ConfigExport) *ConfigExport {
|
|
||||||
if len(exports) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(exports) == 1 {
|
|
||||||
return exports[0]
|
|
||||||
}
|
|
||||||
var export = exports[0]
|
|
||||||
for i := 1; i < len(exports); i++ {
|
|
||||||
ce := exports[i]
|
|
||||||
for _, config := range ce.configs {
|
|
||||||
if _, ok := export.exist[config.Name]; ok {
|
|
||||||
log.Warn("ConfigExport",
|
|
||||||
log.String("File", ce.xlsxPath),
|
|
||||||
log.String("Sheet", config.Name),
|
|
||||||
log.String("Info", "A configuration with the same name exists, skipped"),
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
export.configs = append(export.configs, config)
|
|
||||||
export.exist[config.Name] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return export
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *ConfigExport) ExportClient(prefix, outputDir string) {
|
|
||||||
for _, config := range slf.configs {
|
|
||||||
config := config
|
|
||||||
if len(prefix) > 0 {
|
|
||||||
config.Prefix = fmt.Sprintf("%s.", prefix)
|
|
||||||
}
|
|
||||||
if err := file.WriterFile(filepath.Join(outputDir, fmt.Sprintf("%s%s.json", config.Prefix, config.Name)), config.JsonClient()); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *ConfigExport) ExportServer(prefix, outputDir string) {
|
|
||||||
for _, config := range slf.configs {
|
|
||||||
config := config
|
|
||||||
if len(prefix) > 0 {
|
|
||||||
config.Prefix = fmt.Sprintf("%s.", prefix)
|
|
||||||
}
|
|
||||||
if err := file.WriterFile(filepath.Join(outputDir, fmt.Sprintf("%s%s.json", config.Prefix, config.Name)), config.JsonServer()); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *ConfigExport) ExportGo(prefix, outputDir string) {
|
|
||||||
if len(prefix) > 0 {
|
|
||||||
for _, config := range slf.configs {
|
|
||||||
config.Prefix = fmt.Sprintf("%s.", prefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slf.exportGoConfig(outputDir)
|
|
||||||
slf.exportGoDefine(outputDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *ConfigExport) exportGoConfig(outputDir string) {
|
|
||||||
var v struct {
|
|
||||||
Package string
|
|
||||||
Configs []*internal.Config
|
|
||||||
}
|
|
||||||
v.Package = filepath.Base(outputDir)
|
|
||||||
|
|
||||||
for _, config := range slf.configs {
|
|
||||||
v.Configs = append(v.Configs, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpl, err := template.New("struct").Parse(internal.GenerateGoConfigTemplate)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if err = tmpl.Execute(&buf, &v); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
var result string
|
|
||||||
_ = str.RangeLine(buf.String(), func(index int, line string) error {
|
|
||||||
if len(strings.TrimSpace(line)) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
result += fmt.Sprintf("%s\n", strings.ReplaceAll(line, "\t\t", "\t"))
|
|
||||||
if len(strings.TrimSpace(line)) == 1 {
|
|
||||||
result += "\n"
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
filePath := filepath.Join(outputDir, "config.go")
|
|
||||||
if err := file.WriterFile(filePath, []byte(result)); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command("gofmt", "-w", filePath)
|
|
||||||
_ = cmd.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *ConfigExport) exportGoDefine(outputDir string) {
|
|
||||||
var v struct {
|
|
||||||
Package string
|
|
||||||
Configs []*internal.Config
|
|
||||||
}
|
|
||||||
v.Package = filepath.Base(outputDir)
|
|
||||||
|
|
||||||
for _, config := range slf.configs {
|
|
||||||
v.Configs = append(v.Configs, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpl, err := template.New("struct").Parse(internal.GenerateGoDefineTemplate)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if err = tmpl.Execute(&buf, &v); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
var result string
|
|
||||||
_ = str.RangeLine(buf.String(), func(index int, line string) error {
|
|
||||||
if len(strings.TrimSpace(line)) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
result += fmt.Sprintf("%s\n", strings.ReplaceAll(line, "\t\t", "\t"))
|
|
||||||
if len(strings.TrimSpace(line)) == 1 {
|
|
||||||
result += "\n"
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
filePath := filepath.Join(outputDir, "config.define.go")
|
|
||||||
if err := file.WriterFile(filePath, []byte(result)); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command("gofmt", "-w", filePath)
|
|
||||||
_ = cmd.Run()
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package configexport_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/kercylan98/minotaur/config"
|
|
||||||
"github.com/kercylan98/minotaur/planner/configexport"
|
|
||||||
"github.com/kercylan98/minotaur/planner/configexport/example"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleNew() {
|
|
||||||
var workdir = "./"
|
|
||||||
files, err := os.ReadDir(workdir)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
var ces []*configexport.ConfigExport
|
|
||||||
for _, file := range files {
|
|
||||||
if file.IsDir() || !strings.HasSuffix(file.Name(), ".xlsx") || strings.HasPrefix(file.Name(), "~") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ces = append(ces, configexport.New(filepath.Join(workdir, file.Name())))
|
|
||||||
}
|
|
||||||
|
|
||||||
c := configexport.Merge(ces...)
|
|
||||||
outDir := filepath.Join(workdir, "example")
|
|
||||||
c.ExportGo("", outDir)
|
|
||||||
c.ExportServer("", outDir)
|
|
||||||
c.ExportClient("", outDir)
|
|
||||||
|
|
||||||
// 下方为配置加载代码
|
|
||||||
// 使用生成的 LoadConfig 函数加载配置
|
|
||||||
config.Init(outDir, example.LoadConfig, example.Refresh)
|
|
||||||
|
|
||||||
fmt.Println("success")
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// success
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
// Package configexport 提供了XLSX配置转换为JSON及Go代码的导表工具实现
|
|
||||||
package configexport
|
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"Id": 1,
|
|
||||||
"Award": {
|
|
||||||
"0": "asd",
|
|
||||||
"1": "12"
|
|
||||||
},
|
|
||||||
"Other": {
|
|
||||||
"0": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "张飞"
|
|
||||||
},
|
|
||||||
"1": {
|
|
||||||
"id": 2,
|
|
||||||
"name": "刘备"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
{
|
|
||||||
"0": {
|
|
||||||
"": {
|
|
||||||
"Award": null,
|
|
||||||
"Other": null,
|
|
||||||
"Id": 0,
|
|
||||||
"Count": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"1": {
|
|
||||||
"b": {
|
|
||||||
"Id": 1,
|
|
||||||
"Count": "b",
|
|
||||||
"Award": {
|
|
||||||
"0": "asd",
|
|
||||||
"1": "12"
|
|
||||||
},
|
|
||||||
"Other": {
|
|
||||||
"0": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "张飞"
|
|
||||||
},
|
|
||||||
"1": {
|
|
||||||
"name": "刘备",
|
|
||||||
"id": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"c": {
|
|
||||||
"Id": 2,
|
|
||||||
"Count": "c",
|
|
||||||
"Award": {
|
|
||||||
"0": "asd",
|
|
||||||
"1": "12"
|
|
||||||
},
|
|
||||||
"Other": {
|
|
||||||
"0": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "张飞"
|
|
||||||
},
|
|
||||||
"1": {
|
|
||||||
"id": 2,
|
|
||||||
"name": "刘备"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"d": {
|
|
||||||
"Id": 2,
|
|
||||||
"Count": "d",
|
|
||||||
"Award": {
|
|
||||||
"0": "asd",
|
|
||||||
"1": "12"
|
|
||||||
},
|
|
||||||
"Other": {
|
|
||||||
"0": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "张飞"
|
|
||||||
},
|
|
||||||
"1": {
|
|
||||||
"id": 2,
|
|
||||||
"name": "刘备"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
// Code generated by minotaur-config-export. DO NOT EDIT.
|
|
||||||
package example
|
|
||||||
|
|
||||||
// IndexConfigDefine 有索引
|
|
||||||
type IndexConfigDefine struct {
|
|
||||||
Id int // 任务ID
|
|
||||||
Count string // 次数
|
|
||||||
Info *IndexConfigInfo // 信息
|
|
||||||
Other map[int]*IndexConfigOther // 信息2
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *IndexConfigDefine) String() string {
|
|
||||||
if data, err := json.Marshal(slf); err == nil {
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return "{}"
|
|
||||||
}
|
|
||||||
|
|
||||||
type IndexConfigInfo struct {
|
|
||||||
Id int
|
|
||||||
Name string
|
|
||||||
Info *IndexConfigInfoInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
type IndexConfigInfoInfo struct {
|
|
||||||
Lv int
|
|
||||||
Exp *IndexConfigInfoInfoExp
|
|
||||||
}
|
|
||||||
|
|
||||||
type IndexConfigInfoInfoExp struct {
|
|
||||||
Mux int
|
|
||||||
Count int
|
|
||||||
}
|
|
||||||
|
|
||||||
type IndexConfigOther struct {
|
|
||||||
Id int
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// EasyConfigDefine 无索引
|
|
||||||
type EasyConfigDefine struct {
|
|
||||||
Id int // 任务ID
|
|
||||||
Info *EasyConfigInfo // 信息
|
|
||||||
Other map[int]*EasyConfigOther // 信息2
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *EasyConfigDefine) String() string {
|
|
||||||
if data, err := json.Marshal(slf); err == nil {
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return "{}"
|
|
||||||
}
|
|
||||||
|
|
||||||
type EasyConfigInfo struct {
|
|
||||||
Id int
|
|
||||||
Name string
|
|
||||||
Info *EasyConfigInfoInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
type EasyConfigInfoInfo struct {
|
|
||||||
Lv int
|
|
||||||
Exp *EasyConfigInfoInfoExp
|
|
||||||
}
|
|
||||||
|
|
||||||
type EasyConfigInfoInfoExp struct {
|
|
||||||
Mux int
|
|
||||||
Count int
|
|
||||||
}
|
|
||||||
|
|
||||||
type EasyConfigOther struct {
|
|
||||||
Id int
|
|
||||||
Name string
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
// Code generated by minotaur-config-export. DO NOT EDIT.
|
|
||||||
package example
|
|
||||||
|
|
||||||
import (
|
|
||||||
jsonIter "github.com/json-iterator/go"
|
|
||||||
"github.com/kercylan98/minotaur/utils/log"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var json = jsonIter.ConfigCompatibleWithStandardLibrary
|
|
||||||
var full map[string]any
|
|
||||||
var (
|
|
||||||
// IndexConfig 有索引
|
|
||||||
IndexConfigSign = "IndexConfig"
|
|
||||||
IndexConfig map[int]map[string]*IndexConfigDefine
|
|
||||||
_IndexConfig map[int]map[string]*IndexConfigDefine
|
|
||||||
// EasyConfig 无索引
|
|
||||||
EasyConfigSign = "EasyConfig"
|
|
||||||
EasyConfig *EasyConfigDefine
|
|
||||||
_EasyConfig *EasyConfigDefine
|
|
||||||
)
|
|
||||||
|
|
||||||
func LoadConfig(handle func(filename string, config any) error) {
|
|
||||||
var err error
|
|
||||||
_IndexConfig = make(map[int]map[string]*IndexConfigDefine)
|
|
||||||
if err = handle("IndexConfig.json", &_IndexConfig); err != nil {
|
|
||||||
log.Err("Config", log.String("Name", "IndexConfig"), log.Bool("Invalid", true), log.Err(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
_EasyConfig = new(EasyConfigDefine)
|
|
||||||
if err = handle("EasyConfig.json", _EasyConfig); err != nil {
|
|
||||||
log.Err("Config", log.String("Name", "EasyConfig"), log.Bool("Invalid", true), log.Err(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh 将加载后的配置刷新到线上
|
|
||||||
func Refresh() {
|
|
||||||
full = make(map[string]any)
|
|
||||||
IndexConfig = _IndexConfig
|
|
||||||
full["IndexConfig"] = IndexConfig
|
|
||||||
EasyConfig = _EasyConfig
|
|
||||||
full["EasyConfig"] = EasyConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultLoad 默认提供的配置加载函数
|
|
||||||
func DefaultLoad(filepath string) {
|
|
||||||
LoadConfig(func(filename string, config any) error {
|
|
||||||
bytes, err := os.ReadFile(filepath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Unmarshal(bytes, &config)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFull 获取所有配置的 map 集合
|
|
||||||
// - 通常用于前端配置通过后端接口获取的情况
|
|
||||||
func GetFull() map[string]any {
|
|
||||||
return full
|
|
||||||
}
|
|
|
@ -1,423 +0,0 @@
|
||||||
package internal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
jsonIter "github.com/json-iterator/go"
|
|
||||||
"github.com/kercylan98/minotaur/utils/geometry/matrix"
|
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
"github.com/kercylan98/minotaur/utils/str"
|
|
||||||
"github.com/kercylan98/minotaur/utils/xlsxtool"
|
|
||||||
"github.com/tealeg/xlsx"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewConfig 定位为空将读取Sheet名称
|
|
||||||
// - 表格中需要严格遵守 描述、名称、类型、导出参数、数据列的顺序
|
|
||||||
func NewConfig(sheet *xlsx.Sheet, exist map[string]bool) (*Config, error) {
|
|
||||||
config := &Config{
|
|
||||||
ignore: "#",
|
|
||||||
excludeFields: map[int]bool{0: true},
|
|
||||||
Exist: exist,
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(sheet.Name, config.ignore) {
|
|
||||||
return nil, ErrReadConfigFailedIgnore
|
|
||||||
}
|
|
||||||
if err := config.initField(sheet); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Exist map[string]bool
|
|
||||||
Prefix string
|
|
||||||
DisplayName string
|
|
||||||
Name string
|
|
||||||
Describe string
|
|
||||||
ExportParam string
|
|
||||||
IndexCount int
|
|
||||||
Fields []*Field
|
|
||||||
|
|
||||||
matrix *matrix.Matrix[*xlsx.Cell]
|
|
||||||
excludeFields map[int]bool // 排除的字段
|
|
||||||
ignore string
|
|
||||||
horizontal bool
|
|
||||||
dataStart int
|
|
||||||
|
|
||||||
dataServer map[any]any
|
|
||||||
dataClient map[any]any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Config) initField(sheet *xlsx.Sheet) error {
|
|
||||||
slf.matrix = xlsxtool.GetSheetMatrix(sheet)
|
|
||||||
var displayName *Position
|
|
||||||
name, indexCount := NewPosition(1, 0), NewPosition(1, 1)
|
|
||||||
if displayName == nil {
|
|
||||||
slf.DisplayName = sheet.Name
|
|
||||||
} else {
|
|
||||||
if value := slf.matrix.Get(displayName.X, displayName.Y); value == nil {
|
|
||||||
return ErrReadConfigFailedWithDisplayName
|
|
||||||
} else {
|
|
||||||
slf.DisplayName = strings.TrimSpace(value.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if name == nil {
|
|
||||||
slf.Name = sheet.Name
|
|
||||||
} else {
|
|
||||||
if value := slf.matrix.Get(name.X, name.Y); value == nil {
|
|
||||||
return ErrReadConfigFailedWithName
|
|
||||||
} else {
|
|
||||||
slf.Name = str.FirstUpper(strings.TrimSpace(value.String()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(sheet.Name, slf.ignore) {
|
|
||||||
return ErrReadConfigFailedIgnore
|
|
||||||
}
|
|
||||||
if hash.Exist(slf.Exist, slf.Name) {
|
|
||||||
return ErrReadConfigFailedSame
|
|
||||||
}
|
|
||||||
|
|
||||||
if indexCount == nil {
|
|
||||||
slf.IndexCount, _ = strconv.Atoi(sheet.Name)
|
|
||||||
} else {
|
|
||||||
if value := slf.matrix.Get(indexCount.X, indexCount.Y); value == nil {
|
|
||||||
return ErrReadConfigFailedWithIndexCount
|
|
||||||
} else {
|
|
||||||
indexCount, err := value.Int()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if indexCount < 0 {
|
|
||||||
return ErrReadConfigFailedWithIndexCountLessThanZero
|
|
||||||
}
|
|
||||||
slf.IndexCount = indexCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
describeStart int
|
|
||||||
horizontal bool
|
|
||||||
fields = make(map[string]bool)
|
|
||||||
dx, dy, nx, ny, tx, ty, ex, ey int
|
|
||||||
)
|
|
||||||
horizontal = slf.IndexCount > 0
|
|
||||||
slf.horizontal = horizontal
|
|
||||||
|
|
||||||
if horizontal {
|
|
||||||
describeStart = 3
|
|
||||||
dy = describeStart
|
|
||||||
ny = dy + 1
|
|
||||||
ty = ny + 1
|
|
||||||
ey = ty + 1
|
|
||||||
slf.dataStart = ey + 1
|
|
||||||
} else {
|
|
||||||
delete(slf.excludeFields, 0)
|
|
||||||
describeStart = 4
|
|
||||||
dy = describeStart
|
|
||||||
ny = dy
|
|
||||||
ty = dy
|
|
||||||
ey = dy
|
|
||||||
nx = dx + 1
|
|
||||||
tx = nx + 1
|
|
||||||
ex = tx + 1
|
|
||||||
slf.dataStart = ey + 1
|
|
||||||
}
|
|
||||||
var index = slf.IndexCount
|
|
||||||
for {
|
|
||||||
var (
|
|
||||||
describe, fieldName, fieldType, exportParam string
|
|
||||||
)
|
|
||||||
var skip bool
|
|
||||||
if value, exist := slf.matrix.GetExist(dx, dy); !exist {
|
|
||||||
skip = true
|
|
||||||
} else {
|
|
||||||
describe = value.String()
|
|
||||||
}
|
|
||||||
if value, exist := slf.matrix.GetExist(nx, ny); !exist {
|
|
||||||
skip = true
|
|
||||||
} else {
|
|
||||||
fieldName = str.FirstUpper(strings.TrimSpace(value.String()))
|
|
||||||
}
|
|
||||||
if value, exist := slf.matrix.GetExist(tx, ty); !exist {
|
|
||||||
skip = true
|
|
||||||
} else {
|
|
||||||
fieldType = strings.TrimSpace(value.String())
|
|
||||||
}
|
|
||||||
if value, exist := slf.matrix.GetExist(ex, ey); !exist {
|
|
||||||
skip = true
|
|
||||||
} else {
|
|
||||||
exportParam = strings.ToLower(strings.TrimSpace(value.String()))
|
|
||||||
}
|
|
||||||
if len(strings.TrimSpace(fieldName))+len(strings.TrimSpace(fieldType))+len(strings.TrimSpace(exportParam)) < 3 {
|
|
||||||
skip = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var field = NewField(slf.Name, fieldName, fieldType)
|
|
||||||
field.Describe = strings.ReplaceAll(describe, "\n", ", ")
|
|
||||||
if strings.HasSuffix(field.Describe, ", ") {
|
|
||||||
field.Describe = field.Describe[:len(field.Describe)-2]
|
|
||||||
}
|
|
||||||
field.ExportParam = exportParam
|
|
||||||
switch field.ExportParam {
|
|
||||||
case "s", "sc", "cs":
|
|
||||||
field.Server = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if horizontal {
|
|
||||||
dx++
|
|
||||||
nx++
|
|
||||||
tx++
|
|
||||||
ex++
|
|
||||||
} else {
|
|
||||||
dy++
|
|
||||||
ny++
|
|
||||||
ty++
|
|
||||||
ey++
|
|
||||||
}
|
|
||||||
|
|
||||||
if !skip {
|
|
||||||
|
|
||||||
field.Ignore = slf.excludeFields[len(slf.Fields)]
|
|
||||||
if !field.Ignore {
|
|
||||||
if strings.HasPrefix(field.Describe, slf.ignore) {
|
|
||||||
field.Ignore = true
|
|
||||||
} else if strings.HasPrefix(field.Name, slf.ignore) {
|
|
||||||
field.Ignore = true
|
|
||||||
} else if strings.HasPrefix(field.Type, slf.ignore) {
|
|
||||||
field.Ignore = true
|
|
||||||
} else if strings.HasPrefix(field.ExportParam, slf.ignore) {
|
|
||||||
field.Ignore = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !field.Ignore {
|
|
||||||
switch exportParam {
|
|
||||||
case "s", "c", "sc", "cs":
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if fields[field.Name] && !field.Ignore {
|
|
||||||
return ErrReadConfigFailedWithNameDuplicate
|
|
||||||
}
|
|
||||||
if index > 0 && !field.Ignore {
|
|
||||||
if _, exist := basicTypeName[field.Type]; !exist {
|
|
||||||
return ErrReadConfigFailedWithIndexTypeException
|
|
||||||
}
|
|
||||||
index--
|
|
||||||
}
|
|
||||||
|
|
||||||
fields[field.Name] = true
|
|
||||||
slf.Fields = append(slf.Fields, field)
|
|
||||||
}
|
|
||||||
|
|
||||||
if horizontal {
|
|
||||||
if dx >= slf.matrix.GetWidth() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if dy >= slf.matrix.GetHeight() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return slf.initData()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Config) initData() error {
|
|
||||||
var x, y int
|
|
||||||
if slf.horizontal {
|
|
||||||
y = slf.dataStart
|
|
||||||
} else {
|
|
||||||
x = 4
|
|
||||||
y = 4
|
|
||||||
}
|
|
||||||
var dataSourceServer = make(map[any]any)
|
|
||||||
var dataSourceClient = make(map[any]any)
|
|
||||||
for {
|
|
||||||
var dataServer = dataSourceServer
|
|
||||||
var dataClient = dataSourceClient
|
|
||||||
var currentIndex = 0
|
|
||||||
|
|
||||||
var lineServer = map[any]any{}
|
|
||||||
var lineClient = map[any]any{}
|
|
||||||
var skip bool
|
|
||||||
var offset int
|
|
||||||
var stop bool
|
|
||||||
for i := 0; i < len(slf.Fields); i++ {
|
|
||||||
if slf.excludeFields[i] {
|
|
||||||
if c := slf.matrix.Get(x+i, y); c != nil && strings.HasPrefix(c.String(), "#") {
|
|
||||||
skip = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
field := slf.Fields[i]
|
|
||||||
if field.Ignore {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var value any
|
|
||||||
var zero bool
|
|
||||||
if slf.horizontal {
|
|
||||||
c := slf.matrix.Get(x+i, y)
|
|
||||||
if c == nil {
|
|
||||||
if currentIndex < slf.IndexCount {
|
|
||||||
stop = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
value = getValueZero(field.SourceType)
|
|
||||||
zero = true
|
|
||||||
} else if currentIndex < slf.IndexCount && len(strings.TrimSpace(c.String())) == 0 {
|
|
||||||
stop = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if !zero {
|
|
||||||
value = getValueWithType(field.SourceType, c.String())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c := slf.matrix.Get(x, y+i+offset)
|
|
||||||
for c == nil {
|
|
||||||
offset++
|
|
||||||
c = slf.matrix.Get(x, y+i+offset)
|
|
||||||
if y+i+offset >= slf.matrix.GetHeight() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value = getValueWithType(field.SourceType, c.String())
|
|
||||||
}
|
|
||||||
switch field.ExportParam {
|
|
||||||
case "s":
|
|
||||||
lineServer[field.Name] = value
|
|
||||||
case "c":
|
|
||||||
lineClient[field.Name] = value
|
|
||||||
case "sc", "cs":
|
|
||||||
lineServer[field.Name] = value
|
|
||||||
lineClient[field.Name] = value
|
|
||||||
default:
|
|
||||||
skip = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentIndex < slf.IndexCount {
|
|
||||||
currentIndex++
|
|
||||||
m, exist := dataServer[value]
|
|
||||||
if !exist {
|
|
||||||
if currentIndex == slf.IndexCount {
|
|
||||||
dataServer[value] = lineServer
|
|
||||||
} else {
|
|
||||||
m = map[any]any{}
|
|
||||||
dataServer[value] = m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if currentIndex < slf.IndexCount {
|
|
||||||
dataServer = m.(map[any]any)
|
|
||||||
}
|
|
||||||
|
|
||||||
m, exist = dataClient[value]
|
|
||||||
if !exist {
|
|
||||||
if currentIndex == slf.IndexCount {
|
|
||||||
dataClient[value] = lineClient
|
|
||||||
} else {
|
|
||||||
m = map[any]any{}
|
|
||||||
dataClient[value] = m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if currentIndex < slf.IndexCount {
|
|
||||||
dataClient = m.(map[any]any)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if stop {
|
|
||||||
break
|
|
||||||
} else if slf.horizontal {
|
|
||||||
y++
|
|
||||||
if y >= slf.matrix.GetHeight() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !skip {
|
|
||||||
slf.dataServer = lineServer
|
|
||||||
slf.dataClient = lineClient
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if slf.horizontal {
|
|
||||||
slf.dataServer = dataSourceServer
|
|
||||||
slf.dataClient = dataSourceClient
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Config) String() string {
|
|
||||||
tmpl, err := template.New("struct").Parse(generateConfigTemplate)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if err = tmpl.Execute(&buf, slf); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
var result string
|
|
||||||
_ = str.RangeLine(buf.String(), func(index int, line string) error {
|
|
||||||
if len(strings.TrimSpace(line)) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
result += fmt.Sprintf("%s\n", strings.ReplaceAll(line, "\t\t", "\t"))
|
|
||||||
if len(strings.TrimSpace(line)) == 1 {
|
|
||||||
result += "\n"
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Config) JsonServer() []byte {
|
|
||||||
buffer := &bytes.Buffer{}
|
|
||||||
encoder := jsonIter.NewEncoder(buffer)
|
|
||||||
encoder.SetEscapeHTML(false)
|
|
||||||
encoder.SetIndent("", " ")
|
|
||||||
_ = encoder.Encode(slf.dataServer)
|
|
||||||
return buffer.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Config) JsonClient() []byte {
|
|
||||||
buffer := &bytes.Buffer{}
|
|
||||||
encoder := jsonIter.NewEncoder(buffer)
|
|
||||||
encoder.SetEscapeHTML(false)
|
|
||||||
encoder.SetIndent("", " ")
|
|
||||||
_ = encoder.Encode(slf.dataClient)
|
|
||||||
return buffer.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Config) GetVariable() string {
|
|
||||||
var result string
|
|
||||||
var count int
|
|
||||||
if slf.IndexCount > 0 {
|
|
||||||
for _, field := range slf.Fields {
|
|
||||||
if field.Ignore {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result += fmt.Sprintf("map[%s]", field.Type)
|
|
||||||
count++
|
|
||||||
if count >= slf.IndexCount {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s*%sDefine", result, slf.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Config) GetVariableGen() string {
|
|
||||||
if slf.IndexCount == 0 {
|
|
||||||
return fmt.Sprintf("new(%s)", strings.TrimPrefix(slf.GetVariable(), "*"))
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("make(%s)", slf.GetVariable())
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package internal
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrReadConfigFailedIgnore = errors.New("read config skip ignore")
|
|
||||||
ErrReadConfigFailedSame = errors.New("read config skip, same name")
|
|
||||||
ErrReadConfigFailedWithDisplayName = errors.New("read config display name failed, can not found position")
|
|
||||||
ErrReadConfigFailedWithName = errors.New("read config name failed, can not found position")
|
|
||||||
ErrReadConfigFailedWithIndexCount = errors.New("read config index count failed, can not found position")
|
|
||||||
ErrReadConfigFailedWithIndexCountLessThanZero = errors.New("read config index count failed, value less than zero")
|
|
||||||
ErrReadConfigFailedWithNameDuplicate = errors.New("read config index count failed, duplicate field names")
|
|
||||||
ErrReadConfigFailedWithIndexTypeException = errors.New("read config index count failed, the index type is only allowed to be the basic type")
|
|
||||||
)
|
|
|
@ -1,146 +0,0 @@
|
||||||
package internal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"github.com/kercylan98/minotaur/utils/str"
|
|
||||||
"html/template"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewField(configName, fieldName, fieldType string) *Field {
|
|
||||||
sourceType := fieldType
|
|
||||||
var handleStruct = func(configName, fieldType string) []*Field {
|
|
||||||
var fs []*Field
|
|
||||||
var s = strings.TrimSuffix(strings.TrimPrefix(fieldType, "{"), "}")
|
|
||||||
var fields []string
|
|
||||||
var field string
|
|
||||||
var leftBrackets []int
|
|
||||||
for i, c := range s {
|
|
||||||
switch c {
|
|
||||||
case ',':
|
|
||||||
if len(leftBrackets) == 0 {
|
|
||||||
fields = append(fields, field)
|
|
||||||
field = ""
|
|
||||||
} else {
|
|
||||||
field += string(c)
|
|
||||||
}
|
|
||||||
case '{':
|
|
||||||
leftBrackets = append(leftBrackets, i)
|
|
||||||
field += string(c)
|
|
||||||
case '}':
|
|
||||||
leftBrackets = leftBrackets[:len(leftBrackets)-1]
|
|
||||||
field += string(c)
|
|
||||||
if len(leftBrackets) == 0 {
|
|
||||||
fields = append(fields, field)
|
|
||||||
field = ""
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
field += string(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(field) > 0 {
|
|
||||||
fields = append(fields, field)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fieldInfo := range fields {
|
|
||||||
fieldName, fieldType := str.KV(strings.TrimSpace(fieldInfo), ":")
|
|
||||||
fs = append(fs, NewField(configName, str.FirstUpper(fieldName), fieldType))
|
|
||||||
|
|
||||||
}
|
|
||||||
return fs
|
|
||||||
}
|
|
||||||
|
|
||||||
var fs []*Field
|
|
||||||
var sliceField *Field
|
|
||||||
if t, exist := basicTypeName[fieldType]; exist {
|
|
||||||
fieldType = t
|
|
||||||
goto end
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(fieldType, "[]") {
|
|
||||||
fieldType = strings.TrimPrefix(fieldType, "[]")
|
|
||||||
if t, exist := basicTypeName[fieldType]; exist {
|
|
||||||
fieldType = fmt.Sprintf("map[int]%s", t)
|
|
||||||
} else {
|
|
||||||
sliceField = NewField(configName, fieldName, fieldType)
|
|
||||||
fieldType = fmt.Sprintf("map[int]*%s", configName+fieldName)
|
|
||||||
}
|
|
||||||
goto end
|
|
||||||
}
|
|
||||||
|
|
||||||
fs = handleStruct(configName+fieldName, fieldType)
|
|
||||||
fieldType = fmt.Sprintf("*%s", configName+fieldName)
|
|
||||||
|
|
||||||
end:
|
|
||||||
{
|
|
||||||
return &Field{
|
|
||||||
Name: fieldName,
|
|
||||||
Type: fieldType,
|
|
||||||
SourceType: sourceType,
|
|
||||||
TypeNotStar: strings.TrimPrefix(fieldType, "*"),
|
|
||||||
Fields: fs,
|
|
||||||
SliceField: sliceField,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Field struct {
|
|
||||||
Name string
|
|
||||||
Type string
|
|
||||||
SourceType string
|
|
||||||
TypeNotStar string
|
|
||||||
Fields []*Field
|
|
||||||
SliceField *Field
|
|
||||||
ExportParam string
|
|
||||||
Server bool
|
|
||||||
Describe string
|
|
||||||
Ignore bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Field) Struct() string {
|
|
||||||
if len(slf.Fields) == 0 && slf.SliceField == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
tmpl, err := template.New("struct").Parse(generateGoStructTemplate)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if len(slf.Fields) > 0 {
|
|
||||||
if err = tmpl.Execute(&buf, slf); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
} else if slf.SliceField != nil {
|
|
||||||
if err = tmpl.Execute(&buf, slf.SliceField); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s := buf.String()
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Field) String() string {
|
|
||||||
if len(slf.Fields) == 0 && slf.SliceField == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
tmpl, err := template.New("struct").Parse(generateGoStructTemplate)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if len(slf.Fields) > 0 {
|
|
||||||
if err = tmpl.Execute(&buf, slf); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
} else if slf.SliceField != nil {
|
|
||||||
if err = tmpl.Execute(&buf, slf.SliceField); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s := buf.String()
|
|
||||||
for _, field := range slf.Fields {
|
|
||||||
s += fmt.Sprintf("%s", field.String())
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
|
@ -1,304 +0,0 @@
|
||||||
package internal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kercylan98/minotaur/utils/str"
|
|
||||||
"github.com/tidwall/gjson"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var basicTypeName = map[string]string{
|
|
||||||
"string": "string",
|
|
||||||
"int": "int",
|
|
||||||
"int8": "int8",
|
|
||||||
"int16": "int16",
|
|
||||||
"int32": "int32",
|
|
||||||
"int64": "int64",
|
|
||||||
"uint": "uint",
|
|
||||||
"uint8": "uint8",
|
|
||||||
"uint16": "uint16",
|
|
||||||
"uint32": "uint32",
|
|
||||||
"uint64": "uint64",
|
|
||||||
"float32": "float32",
|
|
||||||
"float64": "float64",
|
|
||||||
"float": "float64",
|
|
||||||
"double": "float64",
|
|
||||||
"number": "float64",
|
|
||||||
"byte": "byte",
|
|
||||||
"rune": "rune",
|
|
||||||
"bool": "bool",
|
|
||||||
"boolean": "bool",
|
|
||||||
}
|
|
||||||
|
|
||||||
var basicType = map[string]func(fieldValue string) any{
|
|
||||||
"string": withStringType,
|
|
||||||
"int": withIntType,
|
|
||||||
"int8": withInt8Type,
|
|
||||||
"int16": withInt16Type,
|
|
||||||
"int32": withInt32Type,
|
|
||||||
"int64": withInt64Type,
|
|
||||||
"uint": withUintType,
|
|
||||||
"uint8": withUint8Type,
|
|
||||||
"uint16": withUint16Type,
|
|
||||||
"uint32": withUint32Type,
|
|
||||||
"uint64": withUint64Type,
|
|
||||||
"float32": withFloat32Type,
|
|
||||||
"float64": withFloat64Type,
|
|
||||||
"float": withFloat64Type,
|
|
||||||
"double": withFloat64Type,
|
|
||||||
"number": withFloat64Type,
|
|
||||||
"byte": withByteType,
|
|
||||||
"rune": withRuneType,
|
|
||||||
"bool": withBoolType,
|
|
||||||
"boolean": withBoolType,
|
|
||||||
}
|
|
||||||
|
|
||||||
func getValueZero(fileType string) any {
|
|
||||||
switch basicTypeName[fileType] {
|
|
||||||
case "string":
|
|
||||||
return getValueWithType(fileType, "")
|
|
||||||
case "int":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "int8":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "int16":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "int32":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "int64":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "uint":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "uint8":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "uint16":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "uint32":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "uint64":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "float32":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "float64":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "byte":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "rune":
|
|
||||||
return getValueWithType(fileType, "0")
|
|
||||||
case "bool":
|
|
||||||
return getValueWithType(fileType, "false")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getValueWithType(fieldType string, fieldValue string) any {
|
|
||||||
fieldType = strings.ToLower(strings.TrimSpace(fieldType))
|
|
||||||
handle, exist := basicType[fieldType]
|
|
||||||
if exist {
|
|
||||||
return handle(fieldValue)
|
|
||||||
} else {
|
|
||||||
return withStructType(fieldType, fieldValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func withStructType(fieldType string, fieldValue string) any {
|
|
||||||
// {id:int,name:string,info:{lv:int,exp:int}}
|
|
||||||
if strings.HasPrefix(fieldType, "[]") {
|
|
||||||
return withSliceType(fieldType, fieldValue)
|
|
||||||
} else if !strings.HasPrefix(fieldType, "{") || !strings.HasSuffix(fieldType, "}") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var s = strings.TrimSuffix(strings.TrimPrefix(fieldType, "{"), "}")
|
|
||||||
var data = map[any]any{}
|
|
||||||
var fields []string
|
|
||||||
var field string
|
|
||||||
var leftBrackets []int
|
|
||||||
for i, c := range s {
|
|
||||||
switch c {
|
|
||||||
case ',':
|
|
||||||
if len(leftBrackets) == 0 {
|
|
||||||
fields = append(fields, field)
|
|
||||||
field = ""
|
|
||||||
} else {
|
|
||||||
field += string(c)
|
|
||||||
}
|
|
||||||
case '{':
|
|
||||||
leftBrackets = append(leftBrackets, i)
|
|
||||||
field += string(c)
|
|
||||||
case '}':
|
|
||||||
leftBrackets = leftBrackets[:len(leftBrackets)-1]
|
|
||||||
field += string(c)
|
|
||||||
if len(leftBrackets) == 0 {
|
|
||||||
fields = append(fields, field)
|
|
||||||
field = ""
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
field += string(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(field) > 0 {
|
|
||||||
fields = append(fields, field)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fieldInfo := range fields {
|
|
||||||
fieldName, fieldType := str.KV(strings.TrimSpace(fieldInfo), ":")
|
|
||||||
handle, exist := basicType[fieldType]
|
|
||||||
if exist {
|
|
||||||
data[fieldName] = handle(gjson.Get(fieldValue, fieldName).String())
|
|
||||||
} else {
|
|
||||||
data[fieldName] = withStructType(fieldType, gjson.Get(fieldValue, fieldName).String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
func withSliceType(fieldType string, fieldValue string) any {
|
|
||||||
if !strings.HasPrefix(fieldType, "[]") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
t := strings.TrimPrefix(fieldType, "[]")
|
|
||||||
var data = map[any]any{}
|
|
||||||
gjson.ForEachLine(fieldValue, func(line gjson.Result) bool {
|
|
||||||
line.ForEach(func(key, value gjson.Result) bool {
|
|
||||||
handle, exist := basicType[t]
|
|
||||||
if exist {
|
|
||||||
data[len(data)] = handle(value.String())
|
|
||||||
} else {
|
|
||||||
data[len(data)] = withStructType(t, value.String())
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
func withStringType(fieldValue string) any {
|
|
||||||
return fieldValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func withIntType(fieldValue string) any {
|
|
||||||
value, _ := strconv.Atoi(fieldValue)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func withInt8Type(fieldValue string) any {
|
|
||||||
value, _ := strconv.Atoi(fieldValue)
|
|
||||||
if value < 0 {
|
|
||||||
return int8(0)
|
|
||||||
} else if value > math.MaxInt8 {
|
|
||||||
return int8(math.MaxInt8)
|
|
||||||
}
|
|
||||||
return int8(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withInt16Type(fieldValue string) any {
|
|
||||||
value, _ := strconv.Atoi(fieldValue)
|
|
||||||
if value < 0 {
|
|
||||||
return int16(0)
|
|
||||||
} else if value > math.MaxInt16 {
|
|
||||||
return int16(math.MaxInt16)
|
|
||||||
}
|
|
||||||
return int16(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withInt32Type(fieldValue string) any {
|
|
||||||
value, _ := strconv.Atoi(fieldValue)
|
|
||||||
if value < 0 {
|
|
||||||
return int32(0)
|
|
||||||
} else if value > math.MaxInt32 {
|
|
||||||
return int32(math.MaxInt32)
|
|
||||||
}
|
|
||||||
return int32(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withInt64Type(fieldValue string) any {
|
|
||||||
value, _ := strconv.ParseInt(fieldValue, 10, 64)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func withUintType(fieldValue string) any {
|
|
||||||
value, _ := strconv.Atoi(fieldValue)
|
|
||||||
if value < 0 {
|
|
||||||
return uint(0)
|
|
||||||
}
|
|
||||||
return uint(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withUint8Type(fieldValue string) any {
|
|
||||||
value, _ := strconv.Atoi(fieldValue)
|
|
||||||
if value < 0 {
|
|
||||||
return uint8(0)
|
|
||||||
} else if value > math.MaxUint8 {
|
|
||||||
return uint8(math.MaxUint8)
|
|
||||||
}
|
|
||||||
return uint8(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withUint16Type(fieldValue string) any {
|
|
||||||
value, _ := strconv.Atoi(fieldValue)
|
|
||||||
if value < 0 {
|
|
||||||
return uint16(0)
|
|
||||||
} else if value > math.MaxUint16 {
|
|
||||||
return uint16(math.MaxUint16)
|
|
||||||
}
|
|
||||||
return uint16(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withUint32Type(fieldValue string) any {
|
|
||||||
value, _ := strconv.Atoi(fieldValue)
|
|
||||||
if value < 0 {
|
|
||||||
return uint32(0)
|
|
||||||
} else if value > math.MaxUint32 {
|
|
||||||
return uint32(math.MaxUint32)
|
|
||||||
}
|
|
||||||
return uint32(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withUint64Type(fieldValue string) any {
|
|
||||||
value, _ := strconv.ParseInt(fieldValue, 10, 64)
|
|
||||||
return uint64(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withFloat32Type(fieldValue string) any {
|
|
||||||
value, _ := strconv.ParseFloat(fieldValue, 32)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func withFloat64Type(fieldValue string) any {
|
|
||||||
value, _ := strconv.ParseFloat(fieldValue, 64)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func withByteType(fieldValue string) any {
|
|
||||||
value, _ := strconv.Atoi(fieldValue)
|
|
||||||
if value < 0 {
|
|
||||||
return byte(0)
|
|
||||||
} else if value > math.MaxUint8 {
|
|
||||||
return byte(math.MaxInt8)
|
|
||||||
}
|
|
||||||
return byte(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withRuneType(fieldValue string) any {
|
|
||||||
value, _ := strconv.Atoi(fieldValue)
|
|
||||||
if value < math.MinInt32 {
|
|
||||||
return rune(0)
|
|
||||||
} else if value > math.MaxInt32 {
|
|
||||||
return rune(math.MaxInt32)
|
|
||||||
}
|
|
||||||
return rune(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withBoolType(fieldValue string) any {
|
|
||||||
switch fieldValue {
|
|
||||||
case "0", "false", "!":
|
|
||||||
return false
|
|
||||||
case "1", "true", "&":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package internal
|
|
||||||
|
|
||||||
func NewPosition(x, y int) *Position {
|
|
||||||
return &Position{
|
|
||||||
X: x,
|
|
||||||
Y: y,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Position struct {
|
|
||||||
X int
|
|
||||||
Y int
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
package internal
|
|
||||||
|
|
||||||
const (
|
|
||||||
generateConfigTemplate = `
|
|
||||||
|
|
||||||
// {{.Name}}Define {{.DisplayName}}
|
|
||||||
type {{.Name}}Define struct {
|
|
||||||
{{range $index, $value := .Fields}}{{if eq $value.Server true}}{{if eq $value.Ignore false}}{{$value.Name}} {{$value.Type}} // {{$value.Describe}}{{end}}{{end}}
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *{{.Name}}Define) String() string {
|
|
||||||
if data, err := json.Marshal(slf); err == nil {
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
return "{}"
|
|
||||||
}
|
|
||||||
|
|
||||||
{{range $index, $value := .Fields}}{{$value}}{{end}}
|
|
||||||
`
|
|
||||||
|
|
||||||
generateGoStructTemplate = `
|
|
||||||
{{if eq .Ignore false}}
|
|
||||||
type {{.TypeNotStar}} struct {
|
|
||||||
{{range $index, $value := .Fields}}
|
|
||||||
{{$value.Name}} {{$value.Type}}
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
`
|
|
||||||
|
|
||||||
GenerateGoConfigTemplate = `// Code generated by minotaur-config-export. DO NOT EDIT.
|
|
||||||
|
|
||||||
package {{.Package}}
|
|
||||||
|
|
||||||
import (
|
|
||||||
jsonIter "github.com/json-iterator/go"
|
|
||||||
"github.com/kercylan98/minotaur/utils/log"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var json = jsonIter.ConfigCompatibleWithStandardLibrary
|
|
||||||
var full map[string]any
|
|
||||||
|
|
||||||
var (
|
|
||||||
{{range $index, $config := .Configs}}
|
|
||||||
// {{$config.Name}}Sign {{$config.DisplayName}}签名
|
|
||||||
{{$config.Name}}Sign = "{{$config.Name}}"
|
|
||||||
// {{$config.Name}} {{$config.DisplayName}}
|
|
||||||
{{$config.Name}} {{$config.GetVariable}}
|
|
||||||
_{{$config.Name}} {{$config.GetVariable}}
|
|
||||||
{{end}}
|
|
||||||
)
|
|
||||||
|
|
||||||
func LoadConfig(handle func(filename string, config any) error) {
|
|
||||||
var err error
|
|
||||||
{{range $index, $config := .Configs}}
|
|
||||||
_{{$config.Name}} = {{$config.GetVariableGen}}
|
|
||||||
{{if eq $config.IndexCount 0}}
|
|
||||||
if err = handle("{{$config.Prefix}}{{$config.Name}}.json", _{{$config.Name}}); err != nil {
|
|
||||||
log.Err("Config", log.String("Name", "{{$config.Name}}"), log.Bool("Invalid", true), log.Err(err))
|
|
||||||
}
|
|
||||||
{{else}}
|
|
||||||
if err = handle("{{$config.Prefix}}{{$config.Name}}.json", &_{{$config.Name}}); err != nil {
|
|
||||||
log.Err("Config", log.String("Name", "{{$config.Name}}"), log.Bool("Invalid", true), log.Err(err))
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh 将加载后的配置刷新到线上
|
|
||||||
func Refresh() {
|
|
||||||
full = make(map[string]any)
|
|
||||||
{{range $index, $config := .Configs}}
|
|
||||||
{{$config.Name}} = _{{$config.Name}}
|
|
||||||
full["{{$config.Name}}"] = {{$config.Name}}
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultLoad 默认提供的配置加载函数
|
|
||||||
func DefaultLoad(filepath string) {
|
|
||||||
LoadConfig(func(filename string, config any) error {
|
|
||||||
bytes, err := os.ReadFile(filepath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return json.Unmarshal(bytes, &config)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFull 获取所有配置的 map 集合
|
|
||||||
// - 通常用于前端配置通过后端接口获取的情况
|
|
||||||
func GetFull() map[string]any {
|
|
||||||
return full
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
GenerateGoDefineTemplate = `// Code generated by minotaur-config-export. DO NOT EDIT.
|
|
||||||
|
|
||||||
package {{.Package}}
|
|
||||||
|
|
||||||
{{range $index, $config := .Configs}}
|
|
||||||
{{$config.String}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
`
|
|
||||||
)
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package pce
|
||||||
|
|
||||||
|
type Config interface {
|
||||||
|
// GetConfigName 配置名称
|
||||||
|
GetConfigName() string
|
||||||
|
// GetDisplayName 配置显示名称
|
||||||
|
GetDisplayName() string
|
||||||
|
// GetDescription 配置描述
|
||||||
|
GetDescription() string
|
||||||
|
// GetIndexCount 索引数量
|
||||||
|
GetIndexCount() int
|
||||||
|
// GetFields 获取字段
|
||||||
|
GetFields() []dataField
|
||||||
|
// GetData 获取数据
|
||||||
|
GetData() [][]dataInfo
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
package pce
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/str"
|
||||||
|
"github.com/tealeg/xlsx"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewIndexXlsxConfig(sheet *xlsx.Sheet) *XlsxIndexConfig {
|
||||||
|
config := &XlsxIndexConfig{
|
||||||
|
sheet: sheet,
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// XlsxIndexConfig 内置的 Xlsx 配置
|
||||||
|
type XlsxIndexConfig struct {
|
||||||
|
sheet *xlsx.Sheet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *XlsxIndexConfig) GetConfigName() string {
|
||||||
|
return str.FirstUpper(strings.TrimSpace(slf.sheet.Rows[0].Cells[1].String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *XlsxIndexConfig) GetDisplayName() string {
|
||||||
|
return slf.sheet.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *XlsxIndexConfig) GetDescription() string {
|
||||||
|
return "暂无描述"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *XlsxIndexConfig) GetIndexCount() int {
|
||||||
|
index, err := slf.sheet.Rows[1].Cells[1].Int()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *XlsxIndexConfig) GetFields() []dataField {
|
||||||
|
var fields []dataField
|
||||||
|
for x := 1; x < slf.getWidth(); x++ {
|
||||||
|
var (
|
||||||
|
desc = slf.get(x, 3)
|
||||||
|
name = slf.get(x, 4)
|
||||||
|
fieldType = slf.get(x, 5)
|
||||||
|
exportType = slf.get(x, 6)
|
||||||
|
)
|
||||||
|
if desc == nil || name == nil || fieldType == nil || exportType == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(desc.String()) == 0 || len(name.String()) == 0 || len(fieldType.String()) == 0 || len(exportType.String()) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields = append(fields, dataField{
|
||||||
|
Name: name.String(),
|
||||||
|
Type: fieldType.String(),
|
||||||
|
ExportType: exportType.String(),
|
||||||
|
Desc: desc.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *XlsxIndexConfig) GetData() [][]dataInfo {
|
||||||
|
var data [][]dataInfo
|
||||||
|
var fields = slf.GetFields()
|
||||||
|
for y := 7; y < slf.getHeight(); y++ {
|
||||||
|
var line []dataInfo
|
||||||
|
var stop bool
|
||||||
|
for x := 0; x < slf.getWidth(); x++ {
|
||||||
|
if prefixCell := slf.get(x, y); prefixCell != nil {
|
||||||
|
prefix := prefixCell.String()
|
||||||
|
if strings.HasPrefix(prefix, "#") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if x == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var isIndex = x-1 < slf.GetIndexCount()
|
||||||
|
|
||||||
|
var value string
|
||||||
|
if valueCell := slf.get(x, y); valueCell != nil {
|
||||||
|
value = valueCell.String()
|
||||||
|
} else if isIndex {
|
||||||
|
stop = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
valueCell := slf.get(x, y)
|
||||||
|
if valueCell == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(fields) > x-1 {
|
||||||
|
line = append(line, dataInfo{
|
||||||
|
dataField: fields[x-1],
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(line) > 0 {
|
||||||
|
data = append(data, line)
|
||||||
|
}
|
||||||
|
if stop {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// getWidth 获取宽度
|
||||||
|
func (slf *XlsxIndexConfig) getWidth() int {
|
||||||
|
return slf.sheet.MaxCol
|
||||||
|
}
|
||||||
|
|
||||||
|
// getHeight 获取高度
|
||||||
|
func (slf *XlsxIndexConfig) getHeight() int {
|
||||||
|
return slf.sheet.MaxRow
|
||||||
|
}
|
||||||
|
|
||||||
|
// get 获取单元格
|
||||||
|
func (slf *XlsxIndexConfig) get(x, y int) *xlsx.Cell {
|
||||||
|
if x < 0 || y < 0 || y >= len(slf.sheet.Rows) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
row := slf.sheet.Rows[y]
|
||||||
|
if x >= len(row.Cells) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return row.Cells[x]
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package pce_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/planner/pce"
|
||||||
|
"github.com/tealeg/xlsx"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewXlsxIndexConfig(t *testing.T) {
|
||||||
|
f, err := xlsx.OpenFile(`D:\sources\minotaur\planner\ce\template.xlsx`)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
xlsxConfig := pce.NewIndexXlsxConfig(f.Sheets[1])
|
||||||
|
|
||||||
|
loader := pce.NewLoader()
|
||||||
|
loader.BindField(
|
||||||
|
new(pce.Int),
|
||||||
|
new(pce.String),
|
||||||
|
)
|
||||||
|
|
||||||
|
loader.LoadStruct(xlsxConfig)
|
||||||
|
loader.LoadData(xlsxConfig)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package pce
|
||||||
|
|
||||||
|
// DataTmpl 数据导出模板
|
||||||
|
type DataTmpl interface {
|
||||||
|
// Render 渲染模板
|
||||||
|
Render(data map[any]any) (string, error)
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package pce
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/file"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewExporter 创建导出器
|
||||||
|
func NewExporter(filePath string) *Exporter {
|
||||||
|
return &Exporter{
|
||||||
|
filePath: filePath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exporter 导出器
|
||||||
|
type Exporter struct {
|
||||||
|
filePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportStruct 导出结构
|
||||||
|
func (slf *Exporter) ExportStruct(tmpl Tmpl, tmplStruct ...*TmplStruct) error {
|
||||||
|
filePath, err := filepath.Abs(slf.filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
raw, err := tmpl.Render(tmplStruct)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = file.WriterFile(filePath, []byte(raw)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("gofmt", "-w", filePath)
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportData 导出数据
|
||||||
|
func (slf *Exporter) ExportData(tmpl DataTmpl, data map[any]any) error {
|
||||||
|
filePath, err := filepath.Abs(slf.filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
raw, err := tmpl.Render(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = file.WriterFile(filePath, []byte(raw)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package pce
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/super"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Field 基本字段类型接口
|
||||||
|
type Field interface {
|
||||||
|
// TypeName 字段类型名称
|
||||||
|
TypeName() string
|
||||||
|
// Zero 获取零值
|
||||||
|
Zero() any
|
||||||
|
// Parse 解析
|
||||||
|
Parse(value string) any
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFieldGolangType 获取字段的 Golang 类型
|
||||||
|
func GetFieldGolangType(field Field) string {
|
||||||
|
typeOf := reflect.TypeOf(field).Elem()
|
||||||
|
kind := strings.ToLower(typeOf.Kind().String())
|
||||||
|
name := strings.ToLower(typeOf.Name())
|
||||||
|
return super.If(kind == name, kind, name)
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package pce_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/planner/pce"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetFieldGolangType(t *testing.T) {
|
||||||
|
fmt.Println(pce.GetFieldGolangType(new(pce.String)))
|
||||||
|
}
|
|
@ -0,0 +1,456 @@
|
||||||
|
package pce
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/super"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Int int
|
||||||
|
|
||||||
|
func (slf Int) TypeName() string {
|
||||||
|
return "int"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Int) Zero() any {
|
||||||
|
return int(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Int) Parse(value string) any {
|
||||||
|
return super.StringToInt(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Int8 int8
|
||||||
|
|
||||||
|
func (slf Int8) TypeName() string {
|
||||||
|
return "int8"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Int8) Zero() any {
|
||||||
|
return int8(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Int8) Parse(value string) any {
|
||||||
|
v := super.StringToInt(value)
|
||||||
|
if v < 0 {
|
||||||
|
return int8(0)
|
||||||
|
} else if v > math.MaxInt8 {
|
||||||
|
return int8(math.MaxInt8)
|
||||||
|
}
|
||||||
|
return int8(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Int16 int16
|
||||||
|
|
||||||
|
func (slf Int16) TypeName() string {
|
||||||
|
return "int16"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Int16) Zero() any {
|
||||||
|
return int16(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Int16) Parse(value string) any {
|
||||||
|
v := super.StringToInt(value)
|
||||||
|
if v < 0 {
|
||||||
|
return int16(0)
|
||||||
|
} else if v > math.MaxInt16 {
|
||||||
|
return int16(math.MaxInt16)
|
||||||
|
}
|
||||||
|
return int16(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Int32 int32
|
||||||
|
|
||||||
|
func (slf Int32) TypeName() string {
|
||||||
|
return "int32"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Int32) Zero() any {
|
||||||
|
return int32(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Int32) Parse(value string) any {
|
||||||
|
v := super.StringToInt(value)
|
||||||
|
if v < 0 {
|
||||||
|
return int32(0)
|
||||||
|
} else if v > math.MaxInt32 {
|
||||||
|
return int32(math.MaxInt32)
|
||||||
|
}
|
||||||
|
return int32(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Int64 int64
|
||||||
|
|
||||||
|
func (slf Int64) TypeName() string {
|
||||||
|
return "int64"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Int64) Zero() any {
|
||||||
|
return int64(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Int64) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseInt(value, 10, 64)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Uint uint
|
||||||
|
|
||||||
|
func (slf Uint) TypeName() string {
|
||||||
|
return "uint"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uint) Zero() any {
|
||||||
|
return uint(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uint) Parse(value string) any {
|
||||||
|
v, _ := strconv.Atoi(value)
|
||||||
|
if v < 0 {
|
||||||
|
return uint(0)
|
||||||
|
}
|
||||||
|
return uint(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Uint8 uint8
|
||||||
|
|
||||||
|
func (slf Uint8) TypeName() string {
|
||||||
|
return "uint8"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uint8) Zero() any {
|
||||||
|
return uint8(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uint8) Parse(value string) any {
|
||||||
|
v, _ := strconv.Atoi(value)
|
||||||
|
if v < 0 {
|
||||||
|
return uint8(0)
|
||||||
|
} else if v > math.MaxUint8 {
|
||||||
|
return uint8(math.MaxUint8)
|
||||||
|
}
|
||||||
|
return uint8(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Uint16 uint16
|
||||||
|
|
||||||
|
func (slf Uint16) TypeName() string {
|
||||||
|
return "uint16"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uint16) Zero() any {
|
||||||
|
return uint16(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uint16) Parse(value string) any {
|
||||||
|
v, _ := strconv.Atoi(value)
|
||||||
|
if v < 0 {
|
||||||
|
return uint16(0)
|
||||||
|
} else if v > math.MaxUint16 {
|
||||||
|
return uint16(math.MaxUint16)
|
||||||
|
}
|
||||||
|
return uint16(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Uint32 uint32
|
||||||
|
|
||||||
|
func (slf Uint32) TypeName() string {
|
||||||
|
return "uint32"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uint32) Zero() any {
|
||||||
|
return uint32(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uint32) Parse(value string) any {
|
||||||
|
v, _ := strconv.Atoi(value)
|
||||||
|
if v < 0 {
|
||||||
|
return uint32(0)
|
||||||
|
} else if v > math.MaxUint32 {
|
||||||
|
return uint32(math.MaxUint32)
|
||||||
|
}
|
||||||
|
return uint32(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Uint64 uint64
|
||||||
|
|
||||||
|
func (slf Uint64) TypeName() string {
|
||||||
|
return "uint64"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uint64) Zero() any {
|
||||||
|
return uint64(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uint64) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseUint(value, 10, 64)
|
||||||
|
if v < 0 {
|
||||||
|
return uint64(0)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Float32 float32
|
||||||
|
|
||||||
|
func (slf Float32) TypeName() string {
|
||||||
|
return "float32"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Float32) Zero() any {
|
||||||
|
return float32(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Float32) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseFloat(value, 32)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Float64 float64
|
||||||
|
|
||||||
|
func (slf Float64) TypeName() string {
|
||||||
|
return "float64"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Float64) Zero() any {
|
||||||
|
return float64(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Float64) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseFloat(value, 64)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type String string
|
||||||
|
|
||||||
|
func (slf String) TypeName() string {
|
||||||
|
return "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf String) Zero() any {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf String) Parse(value string) any {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bool bool
|
||||||
|
|
||||||
|
func (slf Bool) TypeName() string {
|
||||||
|
return "bool"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Bool) Zero() any {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Bool) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseBool(value)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Byte byte
|
||||||
|
|
||||||
|
func (slf Byte) TypeName() string {
|
||||||
|
return "byte"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Byte) Zero() any {
|
||||||
|
return byte(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Byte) Parse(value string) any {
|
||||||
|
v, _ := strconv.Atoi(value)
|
||||||
|
if v < 0 {
|
||||||
|
return byte(0)
|
||||||
|
} else if v > math.MaxUint8 {
|
||||||
|
return byte(math.MaxUint8)
|
||||||
|
}
|
||||||
|
return byte(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rune rune
|
||||||
|
|
||||||
|
func (slf Rune) TypeName() string {
|
||||||
|
return "rune"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Rune) Zero() any {
|
||||||
|
return rune(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Rune) Parse(value string) any {
|
||||||
|
v, _ := strconv.Atoi(value)
|
||||||
|
if v < 0 {
|
||||||
|
return rune(0)
|
||||||
|
} else if v > math.MaxInt32 {
|
||||||
|
return rune(math.MaxInt32)
|
||||||
|
}
|
||||||
|
return rune(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Complex64 complex64
|
||||||
|
|
||||||
|
func (slf Complex64) TypeName() string {
|
||||||
|
return "complex64"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Complex64) Zero() any {
|
||||||
|
return complex64(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Complex64) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseComplex(value, 64)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Complex128 complex128
|
||||||
|
|
||||||
|
func (slf Complex128) TypeName() string {
|
||||||
|
return "complex128"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Complex128) Zero() any {
|
||||||
|
return complex128(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Complex128) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseComplex(value, 128)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Uintptr uintptr
|
||||||
|
|
||||||
|
func (slf Uintptr) TypeName() string {
|
||||||
|
return "uintptr"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uintptr) Zero() any {
|
||||||
|
return uintptr(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Uintptr) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseUint(value, 10, 64)
|
||||||
|
return uintptr(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Double float64
|
||||||
|
|
||||||
|
func (slf Double) TypeName() string {
|
||||||
|
return "double"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Double) Zero() any {
|
||||||
|
return float64(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Double) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseFloat(value, 64)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Float float32
|
||||||
|
|
||||||
|
func (slf Float) TypeName() string {
|
||||||
|
return "float"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Float) Zero() any {
|
||||||
|
return float32(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Float) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseFloat(value, 32)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Long int64
|
||||||
|
|
||||||
|
func (slf Long) TypeName() string {
|
||||||
|
return "long"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Long) Zero() any {
|
||||||
|
return int64(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Long) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseInt(value, 10, 64)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Short int16
|
||||||
|
|
||||||
|
func (slf Short) TypeName() string {
|
||||||
|
return "short"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Short) Zero() any {
|
||||||
|
return int16(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Short) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseInt(value, 10, 16)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Char int8
|
||||||
|
|
||||||
|
func (slf Char) TypeName() string {
|
||||||
|
return "char"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Char) Zero() any {
|
||||||
|
return int8(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Char) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseInt(value, 10, 8)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Number float64
|
||||||
|
|
||||||
|
func (slf Number) TypeName() string {
|
||||||
|
return "number"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Number) Zero() any {
|
||||||
|
return float64(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Number) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseFloat(value, 64)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Integer int64
|
||||||
|
|
||||||
|
func (slf Integer) TypeName() string {
|
||||||
|
return "integer"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Integer) Zero() any {
|
||||||
|
return int64(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Integer) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseInt(value, 10, 64)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Boolean bool
|
||||||
|
|
||||||
|
func (slf Boolean) TypeName() string {
|
||||||
|
return "boolean"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Boolean) Zero() any {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf Boolean) Parse(value string) any {
|
||||||
|
v, _ := strconv.ParseBool(value)
|
||||||
|
return v
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
package pce
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/str"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewLoader 创建加载器
|
||||||
|
// - 加载器被用于加载配置表的数据和结构信息
|
||||||
|
func NewLoader() *Loader {
|
||||||
|
return &Loader{
|
||||||
|
fields: make(map[string]Field),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loader 配置加载器
|
||||||
|
type Loader struct {
|
||||||
|
fields map[string]Field
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindField 绑定字段
|
||||||
|
func (slf *Loader) BindField(fields ...Field) {
|
||||||
|
for _, field := range fields {
|
||||||
|
slf.fields[field.TypeName()] = field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadStruct 加载结构
|
||||||
|
func (slf *Loader) LoadStruct(config Config) *TmplStruct {
|
||||||
|
var tmpl = &TmplStruct{
|
||||||
|
Name: str.FirstUpper(config.GetConfigName()),
|
||||||
|
Desc: config.GetDescription(),
|
||||||
|
}
|
||||||
|
indexCount := config.GetIndexCount()
|
||||||
|
for i, field := range config.GetFields() {
|
||||||
|
f := tmpl.addField(tmpl.Name, str.FirstUpper(field.Name), field.Desc, field.Type, slf.fields)
|
||||||
|
if i < indexCount {
|
||||||
|
f.isIndex = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tmpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadData 加载配置并得到配置数据
|
||||||
|
func (slf *Loader) LoadData(config Config) map[any]any {
|
||||||
|
var source = make(map[any]any)
|
||||||
|
var action = source
|
||||||
|
var indexCount = config.GetIndexCount() - 1
|
||||||
|
for _, row := range config.GetData() {
|
||||||
|
var item = make(map[any]any)
|
||||||
|
for index, field := range row {
|
||||||
|
bind, exist := slf.fields[field.Type]
|
||||||
|
var value any
|
||||||
|
if exist {
|
||||||
|
if len(field.Value) == 0 {
|
||||||
|
value = bind.Zero()
|
||||||
|
} else {
|
||||||
|
value = bind.Parse(field.Value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = slf.structInterpreter(field.Type, field.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value != nil {
|
||||||
|
item[field.Name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
if index < indexCount {
|
||||||
|
m, exist := action[value]
|
||||||
|
if !exist {
|
||||||
|
m = map[any]any{}
|
||||||
|
action[value] = m
|
||||||
|
action = m.(map[any]any)
|
||||||
|
} else {
|
||||||
|
action = m.(map[any]any)
|
||||||
|
}
|
||||||
|
} else if index == indexCount {
|
||||||
|
action[value] = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
action = source
|
||||||
|
}
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
// sliceInterpreter 切片解释器
|
||||||
|
func (slf *Loader) sliceInterpreter(fieldType, fieldValue string) any {
|
||||||
|
if !strings.HasPrefix(fieldType, "[]") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t := strings.TrimPrefix(fieldType, "[]")
|
||||||
|
var data = map[any]any{}
|
||||||
|
gjson.ForEachLine(fieldValue, func(line gjson.Result) bool {
|
||||||
|
line.ForEach(func(key, value gjson.Result) bool {
|
||||||
|
field, exist := slf.fields[t]
|
||||||
|
if exist {
|
||||||
|
data[len(data)] = field.Parse(value.String())
|
||||||
|
} else {
|
||||||
|
data[len(data)] = slf.structInterpreter(t, value.String())
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// structInterpreter 结构体解释器
|
||||||
|
// - {id:int,name:string,info:{lv:int,exp:int}}
|
||||||
|
func (slf *Loader) structInterpreter(fieldType, fieldValue string) any {
|
||||||
|
if strings.HasPrefix(fieldType, "[]") {
|
||||||
|
return slf.sliceInterpreter(fieldType, fieldValue)
|
||||||
|
} else if !strings.HasPrefix(fieldType, "{") || !strings.HasSuffix(fieldType, "}") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var s = strings.TrimSuffix(strings.TrimPrefix(fieldType, "{"), "}")
|
||||||
|
var data = map[any]any{}
|
||||||
|
var fields []string
|
||||||
|
var field string
|
||||||
|
var leftBrackets []int
|
||||||
|
for i, c := range s {
|
||||||
|
switch c {
|
||||||
|
case ',':
|
||||||
|
if len(leftBrackets) == 0 {
|
||||||
|
fields = append(fields, field)
|
||||||
|
field = ""
|
||||||
|
} else {
|
||||||
|
field += string(c)
|
||||||
|
}
|
||||||
|
case '{':
|
||||||
|
leftBrackets = append(leftBrackets, i)
|
||||||
|
field += string(c)
|
||||||
|
case '}':
|
||||||
|
leftBrackets = leftBrackets[:len(leftBrackets)-1]
|
||||||
|
field += string(c)
|
||||||
|
if len(leftBrackets) == 0 {
|
||||||
|
fields = append(fields, field)
|
||||||
|
field = ""
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
field += string(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(field) > 0 {
|
||||||
|
fields = append(fields, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fieldInfo := range fields {
|
||||||
|
fieldName, fieldType := str.KV(strings.TrimSpace(fieldInfo), ":")
|
||||||
|
field, exist := slf.fields[fieldType]
|
||||||
|
if exist {
|
||||||
|
data[fieldName] = field.Parse(gjson.Get(fieldValue, fieldName).String())
|
||||||
|
} else {
|
||||||
|
data[fieldName] = slf.structInterpreter(fieldType, gjson.Get(fieldValue, fieldName).String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// dataInfo 配置数据
|
||||||
|
type dataInfo struct {
|
||||||
|
dataField // 字段
|
||||||
|
Value string // 字段值
|
||||||
|
}
|
||||||
|
|
||||||
|
// dataField 配置数据字段
|
||||||
|
type dataField struct {
|
||||||
|
Name string // 字段名称
|
||||||
|
Desc string // 字段描述
|
||||||
|
Type string // 字段类型
|
||||||
|
ExportType string // 导出类型
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
||||||
|
package pce
|
||||||
|
|
||||||
|
// Tmpl 配置结构模板接口
|
||||||
|
type Tmpl interface {
|
||||||
|
// Render 渲染模板
|
||||||
|
Render(templates []*TmplStruct) (string, error)
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package pce
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/hash"
|
||||||
|
"github.com/kercylan98/minotaur/utils/str"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TmplField 模板字段
|
||||||
|
type TmplField struct {
|
||||||
|
Name string // 字段名称
|
||||||
|
Desc string // 字段描述
|
||||||
|
Type string // 字段类型
|
||||||
|
Struct *TmplStruct // 结构类型字段结构信息
|
||||||
|
Index int // 字段索引
|
||||||
|
slice bool // 是否是切片类型
|
||||||
|
isIndex bool // 是否是索引字段
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIndex 是否是索引字段
|
||||||
|
func (slf *TmplField) IsIndex() bool {
|
||||||
|
return slf.isIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsStruct 是否是结构类型
|
||||||
|
func (slf *TmplField) IsStruct() bool {
|
||||||
|
return slf.Struct != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSlice 是否是切片类型
|
||||||
|
func (slf *TmplField) IsSlice() bool {
|
||||||
|
return slf.slice
|
||||||
|
}
|
||||||
|
|
||||||
|
// setStruct 设置结构类型
|
||||||
|
func (slf *TmplField) setStruct(parent, name, desc, fieldType string, fields map[string]Field) {
|
||||||
|
slf.Struct = &TmplStruct{
|
||||||
|
Name: fmt.Sprintf("%s%s", parent, name),
|
||||||
|
Desc: desc,
|
||||||
|
}
|
||||||
|
slf.handleStruct(slf.Struct.Name, fieldType, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleStruct 处理结构类型
|
||||||
|
func (slf *TmplField) handleStruct(fieldName, fieldType string, fields map[string]Field) {
|
||||||
|
if strings.HasPrefix(fieldType, "[]") {
|
||||||
|
slf.handleSlice(fieldName, fieldType, fields)
|
||||||
|
return
|
||||||
|
} else if !strings.HasPrefix(fieldType, "{") || !strings.HasSuffix(fieldType, "}") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var s = strings.TrimSuffix(strings.TrimPrefix(fieldType, "{"), "}")
|
||||||
|
var fs []string
|
||||||
|
var field string
|
||||||
|
var leftBrackets []int
|
||||||
|
for i, c := range s {
|
||||||
|
switch c {
|
||||||
|
case ',':
|
||||||
|
if len(leftBrackets) == 0 {
|
||||||
|
fs = append(fs, field)
|
||||||
|
field = ""
|
||||||
|
} else {
|
||||||
|
field += string(c)
|
||||||
|
}
|
||||||
|
case '{':
|
||||||
|
leftBrackets = append(leftBrackets, i)
|
||||||
|
field += string(c)
|
||||||
|
case '}':
|
||||||
|
leftBrackets = leftBrackets[:len(leftBrackets)-1]
|
||||||
|
field += string(c)
|
||||||
|
if len(leftBrackets) == 0 {
|
||||||
|
fs = append(fs, field)
|
||||||
|
field = ""
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
field += string(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(field) > 0 {
|
||||||
|
fs = append(fs, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fieldInfo := range fs {
|
||||||
|
fieldName, fieldType := str.KV(strings.TrimSpace(fieldInfo), ":")
|
||||||
|
slf.Struct.addField(slf.Struct.Name, str.FirstUpper(fieldName), fieldName, fieldType, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
slf.Type = slf.Struct.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleSlice 处理切片类型
|
||||||
|
func (slf *TmplField) handleSlice(fieldName, fieldType string, fields map[string]Field) {
|
||||||
|
if !strings.HasPrefix(fieldType, "[]") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slf.slice = true
|
||||||
|
t := strings.TrimPrefix(fieldType, "[]")
|
||||||
|
if hash.Exist(fields, t) {
|
||||||
|
slf.Struct = nil
|
||||||
|
slf.Type = t
|
||||||
|
} else {
|
||||||
|
slf.handleStruct(fieldName, t, fields)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package pce
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TmplStruct 模板结构
|
||||||
|
type TmplStruct struct {
|
||||||
|
Name string // 结构名称
|
||||||
|
Desc string // 结构描述
|
||||||
|
Fields []*TmplField // 字段列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// addField 添加字段
|
||||||
|
func (slf *TmplStruct) addField(parent, name, desc, fieldType string, fields map[string]Field) *TmplField {
|
||||||
|
field := &TmplField{
|
||||||
|
Name: name,
|
||||||
|
Desc: desc,
|
||||||
|
Type: fieldType,
|
||||||
|
}
|
||||||
|
if !hash.Exist(fields, fieldType) {
|
||||||
|
field.setStruct(parent, name, desc, fieldType, fields)
|
||||||
|
}
|
||||||
|
slf.Fields = append(slf.Fields, field)
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllChildren 获取所有子结构
|
||||||
|
func (slf *TmplStruct) AllChildren() []*TmplStruct {
|
||||||
|
var children []*TmplStruct
|
||||||
|
for _, field := range slf.Fields {
|
||||||
|
if field.IsStruct() {
|
||||||
|
children = append(children, field.Struct)
|
||||||
|
children = append(children, field.Struct.AllChildren()...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return children
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
package tmpls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/planner/pce"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewGolang 创建一个 Golang 配置导出模板
|
||||||
|
func NewGolang(packageName string) *Golang {
|
||||||
|
return &Golang{
|
||||||
|
Package: packageName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Golang 配置导出模板
|
||||||
|
type Golang struct {
|
||||||
|
Package string
|
||||||
|
Templates []*pce.TmplStruct
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Golang) Render(templates ...*pce.TmplStruct) (string, error) {
|
||||||
|
slf.Templates = templates
|
||||||
|
return render(`// Code generated by minotaur. DO NOT EDIT.
|
||||||
|
package {{.Package}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
jsonIter "github.com/json-iterator/go"
|
||||||
|
"github.com/kercylan98/minotaur/utils/log"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Sign string
|
||||||
|
|
||||||
|
const (
|
||||||
|
{{- range .Templates}}
|
||||||
|
{{.Name}}Sign Sign = "{{.Name}}"
|
||||||
|
{{- end}}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
json = jsonIter.ConfigCompatibleWithStandardLibrary
|
||||||
|
configs map[Sign]any = map[Sign]any{
|
||||||
|
{{- range .Templates}}
|
||||||
|
{{.Name}}Sign: {{.Name}},
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
signs = []Sign{
|
||||||
|
{{- range .Templates}}
|
||||||
|
{{.Name}}Sign,
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
mutex sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
{{- range .Templates}}
|
||||||
|
{{.Name}} {{$.GetVariable .}}
|
||||||
|
{{- end}}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
{{- range .Templates}}
|
||||||
|
_{{.Name}} {{$.GetVariable .}}
|
||||||
|
{{- end}}
|
||||||
|
)
|
||||||
|
|
||||||
|
{{- range .Templates}}
|
||||||
|
type {{$.GetConfigName .}} struct {
|
||||||
|
{{- range .Fields}}
|
||||||
|
{{- if .IsSlice}}
|
||||||
|
{{- if .IsStruct}}
|
||||||
|
{{.Name}} []*{{.Struct.Name}} // {{.Desc}}
|
||||||
|
{{- else}}
|
||||||
|
{{.Name}} []{{.Type}} // {{.Desc}}
|
||||||
|
{{- end}}
|
||||||
|
{{- else}}
|
||||||
|
{{- if .IsStruct}}
|
||||||
|
{{.Name}} *{{.Struct.Name}} // {{.Desc}}
|
||||||
|
{{- else}}
|
||||||
|
{{.Name}} {{.Type}} // {{.Desc}}
|
||||||
|
{{- end}}
|
||||||
|
{{- end}}
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *{{$.GetConfigName .}}) String() string {
|
||||||
|
if data, err := json.Marshal(slf); err == nil {
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
|
{{- range .Templates}}
|
||||||
|
{{- range .AllChildren}}
|
||||||
|
type {{.Name}} struct {
|
||||||
|
{{- range .Fields}}
|
||||||
|
{{- if .IsSlice}}
|
||||||
|
{{- if .IsStruct}}
|
||||||
|
{{.Name}} []*{{.Struct.Name}} // {{.Desc}}
|
||||||
|
{{- else}}
|
||||||
|
{{.Name}} []{{.Type}} // {{.Desc}}
|
||||||
|
{{- end}}
|
||||||
|
{{- else}}
|
||||||
|
{{- if .IsStruct}}
|
||||||
|
{{.Name}} *{{.Struct.Name}} // {{.Desc}}
|
||||||
|
{{- else}}
|
||||||
|
{{.Name}} {{.Type}} // {{.Desc}}
|
||||||
|
{{- end}}
|
||||||
|
{{- end}}
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{- end}}
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
|
// Load 通过自定义的方式加载配置
|
||||||
|
// - 通常建议使用该方法加载配置,因为仅执行一次加解锁
|
||||||
|
func LoadWithHandle(handle func(sign Sign, config any, json jsonIter.API) error) {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
for _, sign := range signs {
|
||||||
|
{{- range .Templates}}
|
||||||
|
temp := make({{$.GetVariable .}})
|
||||||
|
if err := handle(sign, &temp, json);err != nil {
|
||||||
|
log.Error("Config", log.String("Name", "{{.Name}}"), log.Bool("Invalid", true), log.Err(err))
|
||||||
|
}else {
|
||||||
|
_{{.Name}} = temp
|
||||||
|
}
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadIndexConfig 通过 JSON 加载配置
|
||||||
|
func LoadWithJSON(sign Sign, data []byte) {
|
||||||
|
switch sign {
|
||||||
|
{{- range .Templates}}
|
||||||
|
case {{.Name}}Sign:
|
||||||
|
temp := make({{$.GetVariable .}})
|
||||||
|
if err := json.Unmarshal(data, &{{.Name}}); err != nil {
|
||||||
|
log.Error("Config", log.String("Name", "{{.Name}}"), log.Bool("Invalid", true), log.Err(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_{{.Name}} = temp
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh 将加载后的配置刷新到线上
|
||||||
|
func Refresh() {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
{{- range .Templates}}
|
||||||
|
{{.Name}} = _{{.Name}}
|
||||||
|
_{{.Name}} = nil
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
`, slf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Golang) GetVariable(config *pce.TmplStruct) string {
|
||||||
|
var prefix string
|
||||||
|
for _, field := range config.Fields {
|
||||||
|
if field.IsIndex() {
|
||||||
|
prefix += fmt.Sprintf("map[%s]", field.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s*%s", prefix, slf.GetConfigName(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Golang) GetConfigName(config *pce.TmplStruct) string {
|
||||||
|
return strings.ReplaceAll(config.Name, "Config", "Configuration")
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package tmpls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
jsonIter "github.com/json-iterator/go"
|
||||||
|
"github.com/kercylan98/minotaur/utils/str"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewJSON() *JSON {
|
||||||
|
return &JSON{
|
||||||
|
API: jsonIter.ConfigCompatibleWithStandardLibrary,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type JSON struct {
|
||||||
|
jsonIter.API
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *JSON) Render(data map[any]any) (string, error) {
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
encoder := jsonIter.NewEncoder(buffer)
|
||||||
|
encoder.SetEscapeHTML(false)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
err := encoder.Encode(data)
|
||||||
|
if err != nil {
|
||||||
|
return str.None, err
|
||||||
|
}
|
||||||
|
return buffer.String(), nil
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package tmpls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/kercylan98/minotaur/utils/str"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
func render(temp string, o any) (string, error) {
|
||||||
|
tmpl, err := template.New(temp).Parse(temp)
|
||||||
|
if err != nil {
|
||||||
|
return str.None, err
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err = tmpl.Execute(&buf, o); err != nil {
|
||||||
|
return str.None, err
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package super
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// StringToInt 字符串转换为整数
|
||||||
|
func StringToInt(value string) int {
|
||||||
|
i, _ := strconv.Atoi(value)
|
||||||
|
return i
|
||||||
|
}
|
Loading…
Reference in New Issue