├── examples
├── tools
│ ├── protoc
│ ├── protoc.exe
│ ├── lua5.3
│ │ ├── lua53
│ │ ├── lua53.dll
│ │ └── lua53.exe
│ └── LuaProject
│ │ └── TablePreProcess
│ │ ├── global_postprocess.lua
│ │ └── preprocess_complex_test.lua
├── excels
│ ├── base_test.xlsx
│ ├── big_data.xlsx
│ ├── csv_test.csv
│ ├── game_config.xlsx
│ └── complex_test.xlsx
├── projects
│ └── proj_cs_bin
│ │ ├── table_bytes
│ │ ├── base_test.bytes
│ │ ├── big_data.bytes
│ │ ├── complex_test.bytes
│ │ ├── csv_test.bytes
│ │ └── GameConfig.bytes
│ │ ├── Properties
│ │ └── launchSettings.json
│ │ ├── src
│ │ ├── gen
│ │ │ ├── CfgEnums.cs
│ │ │ ├── CfgMath.cs
│ │ │ ├── CfgCommon.cs
│ │ │ ├── Tblcsv_test.cs
│ │ │ ├── TblGameConfig.cs
│ │ │ ├── Tblbig_data.cs
│ │ │ └── Tblcomplex_test.cs
│ │ └── serialization
│ │ │ ├── FieldTag.cs
│ │ │ ├── ByteBufPool.cs
│ │ │ ├── StringUtil.cs
│ │ │ └── ThreadLocalTemporalByteBufPool.cs
│ │ ├── proj_cs_bin.csproj
│ │ ├── proj_cs_bin.sln
│ │ └── Program.cs
├── cs_bin_config
│ ├── scripts
│ │ ├── common
│ │ │ ├── table-lib.js
│ │ │ └── strings.js
│ │ ├── global
│ │ │ └── post_global.js
│ │ ├── ext_field
│ │ │ └── field-vector3.js
│ │ └── ext_table
│ │ │ └── game_config.js
│ ├── table_meta
│ │ ├── csv_test.toml
│ │ ├── game_config.toml
│ │ ├── big_data.toml
│ │ ├── complex_test.toml
│ │ └── base_test.toml
│ └── enum_define
│ │ └── enum_cs.toml
├── json_meta
│ ├── csv_test.toml
│ ├── base_test.toml
│ └── complex_test.toml
├── lua_meta
│ ├── base_test.toml
│ └── complex_test.toml
└── conf
│ └── config.toml
├── doc
└── img
│ ├── after_optimization.png
│ └── before_optimization.png
├── export
├── link.go
├── api
│ └── iexport.go
├── lua
│ └── template_lua.go
├── register.go
├── cs_bin
│ ├── export_code_enum.go
│ ├── export_code_class.go
│ ├── export_cs_bin.go
│ ├── template_test.go
│ └── template_cs_bin.go
├── entry.go
├── common
│ ├── load_util.go
│ └── export_util.go
└── json
│ └── export_json.go
├── convert
├── wrap
│ ├── vars.go
│ ├── ivaluewrap.go
│ ├── wrap_string.go
│ ├── wrap_class.go
│ ├── wrap_bool.go
│ ├── wrap_double.go
│ ├── wrap_float.go
│ ├── wrap_int.go
│ ├── wrap_long.go
│ ├── wrap_ulong.go
│ ├── wrap_uint.go
│ ├── wrap_enum.go
│ ├── wrap_slice.go
│ └── entry.go
├── apiconvert
│ ├── idatavisitor.go
│ └── icodeprinter.go
└── visitor
│ └── util.go
├── constant
├── constant.go
└── version.go
├── serialization
├── ISerializable.go
├── Vector2.go
├── Vector3.go
└── Vector4.go
├── ext
├── apiext
│ ├── iext_post_global.go
│ └── iext_post_table.go
├── ext_field
│ ├── init.go
│ ├── point_int.go
│ ├── kvlist_int_float.go
│ └── kvlist_int_int.go
├── register.go
├── ext_post
│ ├── post_table_goja.go
│ └── post_global_goja.go
└── ext_field_script
│ └── field_goja.go
├── util
└── time.go
├── data
├── source
│ ├── register.go
│ ├── idatasource.go
│ ├── entry.go
│ ├── source_excel.go
│ ├── source_csv.go
│ └── util.go
├── apidata
│ └── imemorytable.go
├── model
│ ├── tablemodel.go
│ └── tableoptimize.go
├── env
│ ├── env_ext_field.go
│ ├── define.go
│ └── env_enum.go
├── check
│ └── check_entry.go
├── enum
│ └── enum.go
├── optimize
│ └── optimize_repeat.go
└── memory_table
│ └── memory_table_common.go
├── field_type
├── iextfieldtype.go
├── public.go
├── table_field_class.go
├── define.go
└── table_field_type.go
├── .gitignore
├── config
├── config_enum.go
├── define.go
├── config_rule.go
├── config.go
└── config_rule_csbin.go
├── meta
├── table_field.go
├── meta_test.go
├── field_parse.go
├── raw_meta.go
├── gen_meta.go
└── table_meta.go
├── main.go
├── flag.go
├── .github
└── workflows
│ └── go.yml
├── go.mod
└── README.md
/examples/tools/protoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/tools/protoc
--------------------------------------------------------------------------------
/examples/tools/protoc.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/tools/protoc.exe
--------------------------------------------------------------------------------
/examples/tools/lua5.3/lua53:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/tools/lua5.3/lua53
--------------------------------------------------------------------------------
/doc/img/after_optimization.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/doc/img/after_optimization.png
--------------------------------------------------------------------------------
/examples/excels/base_test.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/excels/base_test.xlsx
--------------------------------------------------------------------------------
/examples/excels/big_data.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/excels/big_data.xlsx
--------------------------------------------------------------------------------
/export/link.go:
--------------------------------------------------------------------------------
1 | package export
2 |
3 | import (
4 | _ "github.com/821869798/table-export/ext/ext_field"
5 | )
6 |
--------------------------------------------------------------------------------
/doc/img/before_optimization.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/doc/img/before_optimization.png
--------------------------------------------------------------------------------
/examples/excels/csv_test.csv:
--------------------------------------------------------------------------------
1 | id,name,age
2 | ,,
3 | ,,
4 | ,,
5 | id,名字,年龄
6 | 1,王麻子,12
7 | 2,赵四,45
8 | 3,李琦,23
9 |
--------------------------------------------------------------------------------
/examples/excels/game_config.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/excels/game_config.xlsx
--------------------------------------------------------------------------------
/examples/tools/lua5.3/lua53.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/tools/lua5.3/lua53.dll
--------------------------------------------------------------------------------
/examples/tools/lua5.3/lua53.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/tools/lua5.3/lua53.exe
--------------------------------------------------------------------------------
/examples/excels/complex_test.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/excels/complex_test.xlsx
--------------------------------------------------------------------------------
/convert/wrap/vars.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | var (
4 | EmptyStringArray []string
5 | EmptyStringMap map[string]string = make(map[string]string)
6 | )
7 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/table_bytes/base_test.bytes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/projects/proj_cs_bin/table_bytes/base_test.bytes
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/table_bytes/big_data.bytes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/projects/proj_cs_bin/table_bytes/big_data.bytes
--------------------------------------------------------------------------------
/constant/constant.go:
--------------------------------------------------------------------------------
1 | package constant
2 |
3 | // 全局静态配置
4 | const (
5 | ExcelFileSuffix = ".xlsx"
6 | MetaFileSuffix = ".toml"
7 | CommentSymbol = "#" //通用的注释符号
8 | )
9 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/table_bytes/complex_test.bytes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/821869798/table-export/HEAD/examples/projects/proj_cs_bin/table_bytes/complex_test.bytes
--------------------------------------------------------------------------------
/serialization/ISerializable.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | type ISerializable interface {
4 | GetTypeId() int32
5 | Serialize(buf *ByteBuf)
6 | Deserialize(buf *ByteBuf) error
7 | }
8 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "proj_cs_bin": {
4 | "commandName": "Project",
5 | "workingDirectory": "./"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/ext/apiext/iext_post_global.go:
--------------------------------------------------------------------------------
1 | package apiext
2 |
3 | import "github.com/821869798/table-export/data/model"
4 |
5 | type IExtPostGlobal interface {
6 | PostGlobal(tableMap map[string]*model.TableModel) error
7 | }
8 |
--------------------------------------------------------------------------------
/serialization/Vector2.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | type Vector2 struct {
4 | X float32
5 | Y float32
6 | }
7 |
8 | func NewVector2(x float32, y float32) Vector2 {
9 | return Vector2{X: x, Y: y}
10 | }
11 |
--------------------------------------------------------------------------------
/constant/version.go:
--------------------------------------------------------------------------------
1 | package constant
2 |
3 | import (
4 | "github.com/gookit/slog"
5 | )
6 |
7 | var (
8 | Version = "1.1.0"
9 | )
10 |
11 | func PrintBuild() {
12 | slog.Infof("Version:%s", Version)
13 | }
14 |
--------------------------------------------------------------------------------
/ext/apiext/iext_post_table.go:
--------------------------------------------------------------------------------
1 | package apiext
2 |
3 | import "github.com/821869798/table-export/data/model"
4 |
5 | // IExtPostTable 对一家表的后处理
6 | type IExtPostTable interface {
7 | PostTable(model *model.TableModel) error
8 | }
9 |
--------------------------------------------------------------------------------
/util/time.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "github.com/gookit/slog"
5 | "time"
6 | )
7 |
8 | func TimeCost(start time.Time, formatStr string) {
9 | tc := time.Since(start)
10 | slog.Infof(formatStr, tc)
11 | }
12 |
--------------------------------------------------------------------------------
/serialization/Vector3.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | type Vector3 struct {
4 | X float32
5 | Y float32
6 | Z float32
7 | }
8 |
9 | func NewVector3(x float32, y float32, z float32) Vector3 {
10 | return Vector3{X: x, Y: y, Z: z}
11 | }
12 |
--------------------------------------------------------------------------------
/export/api/iexport.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "github.com/821869798/table-export/config"
5 | "github.com/821869798/table-export/meta"
6 | )
7 |
8 | type IExport interface {
9 | Export(config.MetaRuleUnit)
10 | TableMetas() []*meta.RawTableMeta
11 | }
12 |
--------------------------------------------------------------------------------
/data/source/register.go:
--------------------------------------------------------------------------------
1 | package source
2 |
3 | var dataSourceLoads map[string]IDataSource
4 |
5 | func init() {
6 | dataSourceLoads = make(map[string]IDataSource)
7 | dataSourceLoads["excel"] = NewDataSourceExcel()
8 | dataSourceLoads["csv"] = NewDataSourceCsv()
9 | }
10 |
--------------------------------------------------------------------------------
/serialization/Vector4.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | type Vector4 struct {
4 | X float32
5 | Y float32
6 | Z float32
7 | W float32
8 | }
9 |
10 | func NewVector4(x float32, y float32, z float32, w float32) Vector4 {
11 | return Vector4{X: x, Y: y, Z: z, W: w}
12 | }
13 |
--------------------------------------------------------------------------------
/data/source/idatasource.go:
--------------------------------------------------------------------------------
1 | package source
2 |
3 | import (
4 | "github.com/821869798/table-export/data/model"
5 | "github.com/821869798/table-export/meta"
6 | )
7 |
8 | type IDataSource interface {
9 | LoadDataModel(tableMetal *meta.TableMeta) (*model.TableModel, error)
10 | }
11 |
--------------------------------------------------------------------------------
/ext/ext_field/init.go:
--------------------------------------------------------------------------------
1 | package ext_field
2 |
3 | import "github.com/821869798/table-export/ext"
4 |
5 | func init() {
6 | ext.RegisterExtFieldType(NewExtFieldPointInt())
7 | ext.RegisterExtFieldType(NewExtFieldKVListIntInt())
8 | ext.RegisterExtFieldType(NewExtFieldKVListIntFloat())
9 | }
10 |
--------------------------------------------------------------------------------
/field_type/iextfieldtype.go:
--------------------------------------------------------------------------------
1 | package field_type
2 |
3 | type IExtFieldType interface {
4 | // Name 该类型的名字
5 | Name() string
6 | // DefineFile 定义的文件
7 | DefineFile() string
8 | // TableFieldType 字段的类型
9 | TableFieldType() *TableFieldType
10 | // ParseOriginData 通过单个字段string解析数据
11 | ParseOriginData(origin string) (interface{}, error)
12 | }
13 |
--------------------------------------------------------------------------------
/examples/cs_bin_config/scripts/common/table-lib.js:
--------------------------------------------------------------------------------
1 | const eFieldType = {
2 | None: 0,
3 | Int: 1,
4 | UInt: 2,
5 | Long: 3,
6 | ULong: 4,
7 | Bool: 5,
8 | Float: 6,
9 | Double: 7,
10 | String: 8,
11 | Enum: 9,
12 | Slice: 10,
13 | Map: 11,
14 | Class: 12
15 | }
16 |
17 | module.exports = {
18 | eFieldType
19 | }
--------------------------------------------------------------------------------
/examples/tools/LuaProject/TablePreProcess/global_postprocess.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | local TablePostProcess = {}
5 |
6 | ---@class global_postprocess
7 | --全局配置后处理,在所有配置预处理之后
8 | local function global_postprocess(database)
9 | --处理校验或者其他逻辑
10 |
11 |
12 | --也可以删除包内不要的表,database.xx = nil即可(相当于仅做预处理)
13 |
14 | end
15 |
16 |
17 | return global_postprocess
--------------------------------------------------------------------------------
/export/lua/template_lua.go:
--------------------------------------------------------------------------------
1 | package lua
2 |
3 | const templateLua string = `local {{.Name}} = {
4 | {{ template "lua" .Content -}}
5 | }
6 |
7 | return {{.Name}}
8 |
9 | {{- define "lua" -}}
10 | {{- if IsString . -}}
11 | {{- ToString . -}}
12 | {{- else -}}
13 | {{- range $k, $v := . -}}[{{$k}}] = { {{- template "lua" $v -}} },
14 | {{ end -}}
15 | {{- end -}}
16 | {{- end -}}
17 |
18 | `
19 |
--------------------------------------------------------------------------------
/examples/tools/LuaProject/TablePreProcess/preprocess_complex_test.lua:
--------------------------------------------------------------------------------
1 |
2 | local function preprocess_complex_test(data_table)
3 | for k1,v1 in pairs(data_table) do
4 | for k2,v2 in pairs(v1) do
5 | v2.number = v2.number + 1
6 | end
7 | end
8 |
9 | --双key结构开启这个才能支持默认值metatable
10 | data_table.__isdoublekeydata = true
11 |
12 | return data_table
13 | end
14 |
15 | return preprocess_complex_test
--------------------------------------------------------------------------------
/data/source/entry.go:
--------------------------------------------------------------------------------
1 | package source
2 |
3 | import (
4 | "errors"
5 | "github.com/821869798/table-export/data/model"
6 | "github.com/821869798/table-export/meta"
7 | )
8 |
9 | func GetDataModelByType(tableMetal *meta.TableMeta) (*model.TableModel, error) {
10 | loader, ok := dataSourceLoads[tableMetal.SourceType]
11 | if !ok {
12 | return nil, errors.New("can't support source model:" + tableMetal.SourceType)
13 | }
14 |
15 | dataModel, err := loader.LoadDataModel(tableMetal)
16 |
17 | return dataModel, err
18 | }
19 |
--------------------------------------------------------------------------------
/data/apidata/imemorytable.go:
--------------------------------------------------------------------------------
1 | package apidata
2 |
3 | type IMemoryTable interface {
4 | TableName() string
5 | RawDataMapping() map[interface{}]interface{}
6 | RawDataList() []map[string]interface{}
7 | GetRecordByRow(rowIndex int) (map[string]interface{}, error)
8 | GetRecordRecordMap(recordIndex int) map[string]interface{}
9 | GetRecordByKey(keys ...interface{}) map[string]interface{}
10 | RowIndexList() []int
11 | AddExtraData(string, interface{})
12 | RemoveExtraData(string)
13 | GetExtraDataMap() map[string]interface{}
14 | }
15 |
--------------------------------------------------------------------------------
/examples/cs_bin_config/table_meta/csv_test.toml:
--------------------------------------------------------------------------------
1 | target = "csv_test"
2 | mode = ""
3 | source_type = "csv"
4 |
5 | sources = [
6 | { file_name = "csv_test.csv", sheet_name = "" },
7 | ]
8 |
9 | fields = [
10 | { active = true, sname = "id" , tname = "id" , type = "int" , key = 1, desc = "id" },
11 | { active = true, sname = "name" , tname = "name" , type = "string" , key = 0, desc = "名字" },
12 | { active = true, sname = "age" , tname = "age" , type = "int" , key = 0, desc = "年龄" },
13 | ]
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 | .idea
14 |
15 | examples/temp_*
16 | examples/output/
17 | examples/projects/proj_cs_json2bin/.vs
18 | examples/projects/proj_cs_json2bin/obj
19 | examples/projects/proj_cs_json2bin/bin
20 | examples/projects/proj_cs_bin/obj
21 | examples/projects/proj_cs_bin/bin
22 | examples/projects/proj_cs_bin/.vs
23 |
--------------------------------------------------------------------------------
/examples/json_meta/csv_test.toml:
--------------------------------------------------------------------------------
1 |
2 | # 目标名字
3 | target = "csv_test"
4 | mode = ""
5 | source_type = "csv"
6 |
7 | sources = [
8 | { file_name = "csv_test.csv", sheet_name = "" },
9 | ]
10 |
11 |
12 | fields = [
13 | { active = true, sname = "id" , tname = "id" , type = "int" , key = 1, desc = "主id"},
14 | { active = true, sname = "name" , tname = "name" , type = "string" , key = 0, desc = "名字"},
15 | { active = true, sname = "age" , tname = "age" , type = "int" , key = 0, desc = "年龄"},
16 | ]
17 |
--------------------------------------------------------------------------------
/ext/register.go:
--------------------------------------------------------------------------------
1 | package ext
2 |
3 | import (
4 | "github.com/821869798/table-export/field_type"
5 | "strings"
6 | )
7 |
8 | var (
9 | extFieldTypes = make(map[string]field_type.IExtFieldType)
10 | )
11 |
12 | func RegisterExtFieldType(extFieldType field_type.IExtFieldType) {
13 | extFieldTypes[strings.ToLower(extFieldType.Name())] = extFieldType
14 | }
15 |
16 | // GetExistExtFieldType 获取已有类型的扩展字段,方便嵌套,例如一个类包含了另一个类
17 | func GetExistExtFieldType(name string) (field_type.IExtFieldType, bool) {
18 | v, ok := extFieldTypes[strings.ToLower(name)]
19 | return v, ok
20 | }
21 |
--------------------------------------------------------------------------------
/examples/json_meta/base_test.toml:
--------------------------------------------------------------------------------
1 |
2 | # 目标名字
3 | target = "base_test"
4 | mode = ""
5 | source_type = "excel"
6 |
7 | sources = [
8 | { file_name = "base_test.xlsx", sheet_name = "Sheet1" },
9 | ]
10 |
11 |
12 | fields = [
13 | { active = true, sname = "id" , tname = "id" , type = "int" , key = 1, desc = "主id"},
14 | { active = true, sname = "name" , tname = "name" , type = "string" , key = 0, desc = "名字"},
15 | { active = true, sname = "age" , tname = "age" , type = "int" , key = 0, desc = "年龄"},
16 | ]
17 |
--------------------------------------------------------------------------------
/config/config_enum.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type RawMetaEnumConfig struct {
4 | EnumFileName string `toml:"enum_file"`
5 | EnumDefines []*RawMetaEnumDefine `toml:"enums"`
6 | }
7 |
8 | type RawMetaEnumDefine struct {
9 | Name string `toml:"name"` // 枚举的名字
10 | Parse4String bool `toml:"parse_string"` // 是否从string数据解析,否则为int
11 | Values []*RawMetaEnumValue `toml:"values"`
12 | }
13 |
14 | type RawMetaEnumValue struct {
15 | Index int32 `tom:"index"`
16 | Name string `toml:"name"`
17 | Desc string `toml:"desc"`
18 | ValueString string `toml:"string"`
19 | }
20 |
--------------------------------------------------------------------------------
/config/define.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type ExportType int
4 |
5 | const (
6 | ExportType_None ExportType = iota
7 | ExportType_Lua
8 | ExportType_Json
9 | ExportType_CS_Bin
10 | )
11 |
12 | // MetaRuleUnit 普通版本的规则,freestyle
13 | type MetaRuleUnit interface {
14 | RuleExportType() ExportType
15 | }
16 |
17 | // MetaRuleUnitPlus 高级版的规则,支持的特性更多
18 | type MetaRuleUnitPlus interface {
19 | MetaRuleUnit
20 | ActiveOptimizeData() bool
21 | GetEnumFiles() []string
22 | GetEnumDefinePrefix() string
23 | GetClassDefinePrefix() string
24 | GetBuiltinFieldTypes() []string
25 | GetExtFieldTypeScriptPath() []string
26 | GetPostGlobalScriptPath() string
27 | }
28 |
--------------------------------------------------------------------------------
/field_type/public.go:
--------------------------------------------------------------------------------
1 | package field_type
2 |
3 | func NewTableFieldType(fieldType EFieldType) *TableFieldType {
4 | return newTableFieldType(fieldType)
5 | }
6 |
7 | func NewTableFieldEnumType(name string) *TableFieldType {
8 | return newTableFieldEnumType(name)
9 | }
10 |
11 | func NewTableFieldArrayType(value *TableFieldType) *TableFieldType {
12 | return newTableFieldArrayType(value)
13 | }
14 |
15 | func NewTableFieldMapType(key *TableFieldType, value *TableFieldType) *TableFieldType {
16 | return newTableFieldMapType(key, value)
17 | }
18 |
19 | func NewTableFieldClassType(class *TableFieldClass) *TableFieldType {
20 | return newTableFieldClassType(class)
21 | }
22 |
--------------------------------------------------------------------------------
/examples/lua_meta/base_test.toml:
--------------------------------------------------------------------------------
1 |
2 | # 目标名字
3 | target = "base_test"
4 | mode = ""
5 | source_type = "excel"
6 |
7 | sources = [
8 | { file_name = "base_test.xlsx", sheet_name = "Sheet1" },
9 | ]
10 |
11 |
12 | fields = [
13 | { active = true, sname = "id" , tname = "id" , type = "int" , key = 1, desc = "主id"},
14 | { active = true, sname = "name" , tname = "name" , type = "string" , key = 0, desc = "名字"},
15 | { active = true, sname = "age" , tname = "age" , type = "int" , key = 0, desc = "年龄"},
16 | { active = true, sname = "course" , tname = "course" , type = "[]int" , key = 0, desc = "年龄"},
17 | ]
18 |
--------------------------------------------------------------------------------
/meta/table_field.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | import (
4 | "github.com/821869798/table-export/field_type"
5 | )
6 |
7 | type TableField struct {
8 | Source string
9 | Target string
10 | Type *field_type.TableFieldType
11 | TypeString string
12 | Desc string
13 | Key int
14 | }
15 |
16 | func newTableField(rtf *RawTableField) (*TableField, error) {
17 | tf := &TableField{
18 | Source: rtf.Source,
19 | Target: rtf.Target,
20 | TypeString: rtf.Type,
21 | Desc: rtf.Desc,
22 | Key: rtf.Key,
23 | }
24 | tft, err := parseFieldTypeFromString(tf.TypeString)
25 | if err != nil {
26 | return nil, err
27 | }
28 | tf.Type = tft
29 | return tf, nil
30 | }
31 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/src/gen/CfgEnums.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace CfgTable
3 | {
4 |
5 | public enum ItemType
6 | {
7 | // None :
8 | None = 0,
9 | // Resource : 资源类
10 | Resource = 1,
11 | // Package : 礼包
12 | Package = 2,
13 | }
14 |
15 | public enum ConditionType
16 | {
17 | // None :
18 | None = 0,
19 | // Equal : 等于
20 | Equal = 1,
21 | // Greater : 大于
22 | Greater = 2,
23 | // Less : 小于
24 | Less = 3,
25 | // GreaterEqual : 大于等于
26 | GreaterEqual = 4,
27 | // LessEqual : 大于等于
28 | LessEqual = 5,
29 | // NotEqual : 不等于
30 | NotEqual = 10,
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/examples/cs_bin_config/table_meta/game_config.toml:
--------------------------------------------------------------------------------
1 | target = "GameConfig"
2 | mode = ""
3 | source_type = "excel"
4 | post_script = "cs_bin_config/scripts/ext_table/game_config.js"
5 |
6 | sources = [
7 | { file_name = "game_config.xlsx", sheet_name = "Sheet1" },
8 | ]
9 |
10 | fields = [
11 | { active = true, sname = "id" , tname = "id" , type = "int" , key = 1, desc = "id" },
12 | { active = true, sname = "data" , tname = "data" , type = "string" , key = 0, desc = "内容值" },
13 | { active = true, sname = "remark" , tname = "备注" , type = "string" , key = 0, desc = "描述" },
14 | { active = false, sname = "备注" , tname = "备注" , type = "" , key = 0, desc = "" },
15 | ]
16 |
17 | checks = [
18 | ]
19 |
--------------------------------------------------------------------------------
/examples/cs_bin_config/table_meta/big_data.toml:
--------------------------------------------------------------------------------
1 | target = "big_data"
2 | mode = ""
3 | source_type = "excel"
4 |
5 | sources = [
6 | { file_name = "big_data.xlsx", sheet_name = "Sheet1" },
7 | ]
8 |
9 | fields = [
10 | { active = true, sname = "id" , tname = "id" , type = "int" , key = 1, desc = "id" },
11 | { active = true, sname = "name" , tname = "name" , type = "string" , key = 0, desc = "名字" },
12 | { active = true, sname = "age" , tname = "age" , type = "int" , key = 0, desc = "年龄" },
13 | { active = true, sname = "course" , tname = "course" , type = "[]int" , key = 0, desc = "学科id" },
14 | { active = true, sname = "score" , tname = "score" , type = "map[int]int" , key = 0, desc = "成绩组" },
15 | ]
16 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/proj_cs_bin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | enable
7 | enable
8 | True
9 | $(MSBuildProjectDirectory)
10 |
11 |
12 |
13 |
14 |
15 | PreserveNewest
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/export/register.go:
--------------------------------------------------------------------------------
1 | package export
2 |
3 | import (
4 | "github.com/821869798/table-export/config"
5 | "github.com/821869798/table-export/export/api"
6 | "github.com/821869798/table-export/export/cs_bin"
7 | "github.com/821869798/table-export/export/json"
8 | "github.com/821869798/table-export/export/lua"
9 | "github.com/821869798/table-export/meta"
10 | )
11 |
12 | var exportCreators map[config.ExportType]func([]*meta.RawTableMeta, map[string]string) api.IExport
13 |
14 | func init() {
15 | exportCreators = make(map[config.ExportType]func([]*meta.RawTableMeta, map[string]string) api.IExport)
16 | exportCreators[config.ExportType_Lua] = lua.NewExportLua
17 | exportCreators[config.ExportType_Json] = json.NewExportJson
18 | exportCreators[config.ExportType_CS_Bin] = cs_bin.NewExportCSBin
19 | }
20 |
--------------------------------------------------------------------------------
/meta/meta_test.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | import (
4 | "bytes"
5 | "github.com/821869798/fankit/fanpath"
6 | "regexp"
7 | "testing"
8 | "text/template"
9 | )
10 |
11 | func TestMeta(t *testing.T) {
12 | reg := regexp.MustCompile(`^\[\](.+)$`)
13 | result := reg.FindAllStringSubmatch("[]int", -1)
14 | t.Log(result)
15 | }
16 |
17 | func TestTemplate(t *testing.T) {
18 | t1 := template.New("test1")
19 | tmpl, _ := t1.Parse(
20 | `
21 | {{- define "T1"}}ONE {{println .}}{{end}}
22 | {{- define "T2"}}{{template "T1" $}}{{end}}
23 | {{- template "T2" . -}}
24 | `)
25 | bb := &bytes.Buffer{}
26 | _ = tmpl.Execute(bb, "hello world")
27 | t.Log(bb.String())
28 | }
29 |
30 | func TestUtil(t *testing.T) {
31 | _ = fanpath.InitDirAndClearFile(`E:\program\gopath\src\table-export\examples\temp_lua`, `^.*?\.(lua|meta)$`)
32 | }
33 |
--------------------------------------------------------------------------------
/examples/cs_bin_config/scripts/global/post_global.js:
--------------------------------------------------------------------------------
1 | const {eFieldType} = require("../common/table-lib");
2 | const strings = require('../common/strings');
3 |
4 | const ArraySplit = tableEngine.TableConfig.ArraySplit;
5 |
6 | function PostGlobal(tableMap,context) {
7 | console.log("tableCount:" + tableEngine.len(tableMap));
8 |
9 | // test code
10 | const gameConfig = tableEngine.getMapValue(tableMap,"GameConfig")
11 | console.log("heroLevelMax:" + gameConfig.MemTable.GetExtraDataMap()["heroLevelMax"]);
12 |
13 | const baseTest = tableEngine.getMapValue(tableMap,"base_test")
14 | console.log("base_test[1]:" + tableEngine.toJson(gameConfig.MemTable.GetRecordByKey(1)));
15 |
16 | tableEngine.forEach(tableMap,(value, key) => {
17 | //console.log(key);
18 | });
19 | }
20 |
21 | module.exports = PostGlobal;
--------------------------------------------------------------------------------
/field_type/table_field_class.go:
--------------------------------------------------------------------------------
1 | package field_type
2 |
3 | type TableFieldClass struct {
4 | Name string
5 | fields []TableFieldClassField
6 | fieldMapping map[string]TableFieldClassField
7 | }
8 |
9 | type TableFieldClassField struct {
10 | Name string
11 | Type *TableFieldType
12 | }
13 |
14 | func NewTableFieldClass(name string) *TableFieldClass {
15 | return &TableFieldClass{
16 | Name: name,
17 | fieldMapping: make(map[string]TableFieldClassField),
18 | }
19 | }
20 |
21 | func (c *TableFieldClass) AddField(name string, fieldType *TableFieldType) {
22 | field := TableFieldClassField{
23 | Name: name,
24 | Type: fieldType,
25 | }
26 | c.fields = append(c.fields, field)
27 | c.fieldMapping[name] = field
28 | }
29 |
30 | func (c *TableFieldClass) AllFields() []TableFieldClassField {
31 | return c.fields
32 | }
33 |
--------------------------------------------------------------------------------
/data/model/tablemodel.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "github.com/821869798/table-export/data/apidata"
5 | "github.com/821869798/table-export/meta"
6 | )
7 |
8 | // TableModel 表的数据
9 | type TableModel struct {
10 | NameIndexMapping map[string]int //目标字段名到index的索引
11 | NameRow []string //字段名的行
12 | DescRow []string //描述的行
13 | RawData [][]string //数据
14 | Meta *meta.TableMeta
15 | Optimize *TableOptimize
16 | MemTable apidata.IMemoryTable // 内存中转换后的数据,主要是interface{}类型
17 | ClearRecord bool // 是否清除整个表的每行数据
18 | }
19 |
20 | func NewTableModel(meta *meta.TableMeta) *TableModel {
21 | d := &TableModel{
22 | NameIndexMapping: nil,
23 | NameRow: nil,
24 | DescRow: nil,
25 | RawData: nil,
26 | Meta: meta,
27 | ClearRecord: false,
28 | }
29 | return d
30 | }
31 |
--------------------------------------------------------------------------------
/examples/json_meta/complex_test.toml:
--------------------------------------------------------------------------------
1 | target = "complex_test"
2 | mode = ""
3 | source_type = "excel"
4 |
5 | sources = [
6 | { file_name = "complex_test.xlsx", sheet_name = "Sheet1" },
7 | ]
8 |
9 | fields = [
10 | { active = true, sname = "key1" , tname = "key1" , type = "int" , key = 1, desc = "id" },
11 | { active = true, sname = "key2" , tname = "key2" , type = "string" , key = 2, desc = "id2" },
12 | { active = true, sname = "content" , tname = "content" , type = "string" , key = 0, desc = "内容" },
13 | { active = true, sname = "number" , tname = "number" , type = "float" , key = 0, desc = "数字" },
14 | { active = true, sname = "test_list" , tname = "test_list" , type = "[]int" , key = 0, desc = "列表" },
15 | { active = true, sname = "test_map" , tname = "test_map" , type = "map[int]string" , key = 0, desc = "字典" },
16 | ]
17 |
--------------------------------------------------------------------------------
/examples/lua_meta/complex_test.toml:
--------------------------------------------------------------------------------
1 | target = "complex_test"
2 | mode = ""
3 | source_type = "excel"
4 |
5 | sources = [
6 | { file_name = "complex_test.xlsx", sheet_name = "Sheet1" },
7 | ]
8 |
9 | fields = [
10 | { active = true, sname = "key1" , tname = "key1" , type = "int" , key = 1, desc = "id" },
11 | { active = true, sname = "key2" , tname = "key2" , type = "string" , key = 2, desc = "id2" },
12 | { active = true, sname = "content" , tname = "content" , type = "string" , key = 0, desc = "内容" },
13 | { active = true, sname = "number" , tname = "number" , type = "float" , key = 0, desc = "数字" },
14 | { active = true, sname = "test_list" , tname = "test_list" , type = "[]int" , key = 0, desc = "列表" },
15 | { active = true, sname = "test_map" , tname = "test_map" , type = "map[int]string" , key = 0, desc = "字典" },
16 | ]
17 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "github.com/821869798/fankit/fanpath"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/constant"
8 | "github.com/821869798/table-export/export"
9 | "github.com/821869798/table-export/meta"
10 | "github.com/gookit/slog"
11 | )
12 |
13 | func main() {
14 |
15 | slog.SetLogLevel(slog.InfoLevel)
16 |
17 | err := fanpath.InitExecutePath()
18 | if err != nil {
19 | slog.Fatal(err)
20 | }
21 |
22 | flag.Parse()
23 |
24 | if params.help {
25 | usage()
26 | return
27 | }
28 |
29 | if params.version {
30 | constant.PrintBuild()
31 | }
32 |
33 | config.ParseConfig(params.confFile)
34 |
35 | if params.genSource != "" {
36 | genMeta := meta.NewGenMeta(params.genSource)
37 | genMeta.Run()
38 | }
39 |
40 | if params.mode != "" {
41 | entry := export.NewEntry(params.mode, params.extra)
42 | entry.Run()
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/examples/cs_bin_config/table_meta/complex_test.toml:
--------------------------------------------------------------------------------
1 | target = "complex_test"
2 | mode = ""
3 | source_type = "excel"
4 |
5 | sources = [
6 | { file_name = "complex_test.xlsx", sheet_name = "Sheet1" },
7 | ]
8 |
9 | fields = [
10 | { active = true, sname = "key1" , tname = "key1" , type = "int" , key = 1, desc = "id" },
11 | { active = true, sname = "key2" , tname = "key2" , type = "string" , key = 2, desc = "id2" },
12 | { active = true, sname = "content" , tname = "content" , type = "string" , key = 0, desc = "内容" },
13 | { active = true, sname = "number" , tname = "number" , type = "float" , key = 0, desc = "数字" },
14 | { active = true, sname = "test_list" , tname = "test_list" , type = "[]int" , key = 0, desc = "列表" },
15 | { active = true, sname = "test_map" , tname = "test_map" , type = "map[int]string" , key = 0, desc = "字典" },
16 | ]
17 |
--------------------------------------------------------------------------------
/examples/cs_bin_config/enum_define/enum_cs.toml:
--------------------------------------------------------------------------------
1 | enum_file = "CfgEnums"
2 |
3 | [[enums]]
4 | name = "ItemType"
5 | values = [
6 | { index = 0 , name = "None", desc = "" },
7 | { index = 1 , name = "Resource", desc = "资源类" },
8 | { index = 2 , name = "Package", desc = "礼包" }
9 | ]
10 |
11 | [[enums]]
12 | name = "ConditionType"
13 | # 使用String来解析
14 | parse_string = true
15 | values = [
16 | { index = 0 , name = "None", string = "", desc = "" },
17 | { index = 1 , name = "Equal", string = "=", desc = "等于" },
18 | { index = 2 , name = "Greater", string = ">", desc = "大于"},
19 | { index = 3 , name = "Less", string = "<", desc = "小于"},
20 | { index = 4 , name = "GreaterEqual", string = ">=", desc = "大于等于"},
21 | { index = 5 , name = "LessEqual", string = "<=", desc = "大于等于"},
22 | { index = 10, name = "NotEqual", string = "!=", desc = "不等于"},
23 | ]
24 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/table_bytes/csv_test.bytes:
--------------------------------------------------------------------------------
1 | 王麻子赵四李琦-
--------------------------------------------------------------------------------
/field_type/define.go:
--------------------------------------------------------------------------------
1 | package field_type
2 |
3 | type EFieldType int
4 |
5 | const (
6 | EFieldType_None EFieldType = iota
7 | EFieldType_Int
8 | EFieldType_UInt
9 | EFieldType_Long
10 | EFieldType_ULong
11 | EFieldType_Bool
12 | EFieldType_Float
13 | EFieldType_Double
14 | EFieldType_String
15 | EFieldType_Enum
16 | EFieldType_Slice
17 | EFieldType_Map
18 | EFieldType_Class
19 | )
20 |
21 | var baseFieldType = map[EFieldType]*TableFieldType{
22 | EFieldType_Int: &TableFieldType{Type: EFieldType_Int},
23 | EFieldType_UInt: &TableFieldType{Type: EFieldType_UInt},
24 | EFieldType_Long: &TableFieldType{Type: EFieldType_Long},
25 | EFieldType_ULong: &TableFieldType{Type: EFieldType_ULong},
26 | EFieldType_Bool: &TableFieldType{Type: EFieldType_Bool},
27 | EFieldType_Float: &TableFieldType{Type: EFieldType_Float},
28 | EFieldType_Double: &TableFieldType{Type: EFieldType_Double},
29 | EFieldType_String: &TableFieldType{Type: EFieldType_String},
30 | EFieldType_Enum: &TableFieldType{Type: EFieldType_Enum},
31 | }
32 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/table_bytes/GameConfig.bytes:
--------------------------------------------------------------------------------
1 | <d5
--------------------------------------------------------------------------------
/data/env/env_ext_field.go:
--------------------------------------------------------------------------------
1 | package env
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/field_type"
7 | "strings"
8 | )
9 |
10 | func AddExtFieldType(extFieldType field_type.IExtFieldType) error {
11 | name := strings.ToLower(extFieldType.Name())
12 | _, ok := environment.extFieldMapping[name]
13 | if ok {
14 | return errors.New(fmt.Sprintf("ext field_type type name repeated: name[%v] ", name))
15 | }
16 | environment.extFieldMapping[name] = extFieldType
17 | fieldType := extFieldType.TableFieldType()
18 | if fieldType.Type == field_type.EFieldType_Class {
19 | environment.extFieldClassFiles[extFieldType.DefineFile()] = append(environment.extFieldClassFiles[extFieldType.DefineFile()], fieldType)
20 | }
21 | return nil
22 | }
23 |
24 | func GetExtFieldType(name string) (field_type.IExtFieldType, bool) {
25 | v, ok := environment.extFieldMapping[strings.ToLower(name)]
26 | return v, ok
27 | }
28 |
29 | func GetExtFieldClassFiles() map[string][]*field_type.TableFieldType {
30 | return environment.extFieldClassFiles
31 | }
32 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/src/gen/CfgMath.cs:
--------------------------------------------------------------------------------
1 | using Serialization;
2 |
3 | using System.Collections.Generic;
4 |
5 | namespace CfgTable
6 | {
7 |
8 | public partial class PointInt
9 | {
10 | public int x { get; private set; }
11 | public int y { get; private set; }
12 |
13 | public PointInt(ByteBuf _buf)
14 | {
15 | x = _buf.ReadInt();
16 | y = _buf.ReadInt();
17 | PostInit();
18 | }
19 |
20 | ///
21 | /// post process ext class
22 | ///
23 | partial void PostInit();
24 | }
25 |
26 | public partial class TestVector3
27 | {
28 | public float x { get; private set; }
29 | public float y { get; private set; }
30 | public float z { get; private set; }
31 |
32 | public TestVector3(ByteBuf _buf)
33 | {
34 | x = _buf.ReadFloat();
35 | y = _buf.ReadFloat();
36 | z = _buf.ReadFloat();
37 | PostInit();
38 | }
39 |
40 | ///
41 | /// post process ext class
42 | ///
43 | partial void PostInit();
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/convert/wrap/ivaluewrap.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "github.com/821869798/table-export/config"
5 | "github.com/821869798/table-export/convert/apiconvert"
6 | "github.com/821869798/table-export/field_type"
7 | )
8 |
9 | type IValueWrap interface {
10 | OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error)
11 | OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error)
12 | OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error)
13 | FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error)
14 | DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error
15 | DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error
16 | CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
17 | }
18 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/src/serialization/FieldTag.cs:
--------------------------------------------------------------------------------
1 | namespace Serialization
2 | {
3 |
4 | // 把 int,long,string,bool 调整到最小
5 | // 这样 marshal compatible write(field_id << tag_shift | tag_id) < 2^7 能在一个字节
6 | // 内序列化, 优化序列化最终大小
7 | #pragma warning disable CA1720 // 标识符包含类型名称
8 | public static class FieldTag
9 | {
10 | public const int
11 | INT = 0,
12 | LONG = 1,
13 | STRING = 2,
14 | BOOL = 3,
15 |
16 | BYTE = 4,
17 | SHORT = 5,
18 | FSHORT = 6,
19 | FINT = 7,
20 | FLONG = 8,
21 | FLOAT = 9,
22 | DOUBLE = 10,
23 | BYTES = 11,
24 | ARRAY = 12,
25 | LIST = 13,
26 | SET = 14,
27 | MAP = 15,
28 | BEAN = 16,
29 | TEXT = 17,
30 | VECTOR2 = 18,
31 | VECTOR3 = 19,
32 | VECTOR4 = 20,
33 | DYNAMIC_BEAN = 21,
34 |
35 | NOT_USE = 22;
36 |
37 |
38 | public const int TAG_SHIFT = 5;
39 | public const int TAG_MASK = (1 << TAG_SHIFT) - 1;
40 | }
41 | #pragma warning restore CA1720 // 标识符包含类型名称
42 | }
--------------------------------------------------------------------------------
/convert/apiconvert/idatavisitor.go:
--------------------------------------------------------------------------------
1 | package apiconvert
2 |
3 | import (
4 | "github.com/821869798/table-export/data/model"
5 | "github.com/821869798/table-export/field_type"
6 | )
7 |
8 | type IDataVisitor interface {
9 | AcceptTable(dataModel *model.TableModel)
10 | AcceptInt(r int32)
11 | AcceptUInt(r uint32)
12 | AcceptLong(r int64)
13 | AcceptULong(r uint64)
14 | AcceptBool(r bool)
15 | AcceptFloat(r float32)
16 | AcceptDouble(r float64)
17 | AcceptString(r string)
18 | AcceptByte(r byte)
19 | AcceptArray(r []interface{}, ValueType *field_type.TableFieldType)
20 | AcceptStringArray(r []string, ValueType *field_type.TableFieldType)
21 | AcceptStringMap(r map[string]string, KeyType *field_type.TableFieldType, ValueType *field_type.TableFieldType)
22 | AcceptMap(r map[string]interface{}, KeyType *field_type.TableFieldType, ValueType *field_type.TableFieldType)
23 | AcceptCommonMap(r map[interface{}]interface{}, KeyType *field_type.TableFieldType, ValueType *field_type.TableFieldType)
24 | AcceptClass(r map[string]interface{}, class *field_type.TableFieldClass)
25 | AcceptClassString(r map[string]string, class *field_type.TableFieldClass)
26 | AcceptClassNull(class *field_type.TableFieldClass)
27 | }
28 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/src/serialization/ByteBufPool.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Serialization
5 | {
6 | public class ByteBufPool
7 | {
8 | private readonly Stack _bufs = new Stack();
9 |
10 | private readonly int _maxCacheNum;
11 |
12 | private readonly Action _freeAction;
13 |
14 | public ByteBufPool(int maxCacheNum)
15 | {
16 | _maxCacheNum = maxCacheNum;
17 | _freeAction = Free;
18 | }
19 |
20 | public ByteBuf Alloc(int? hintSize)
21 | {
22 | if (_bufs.TryPop(out var result))
23 | {
24 | return result;
25 | }
26 |
27 | return new ByteBuf(hintSize ?? 64, _freeAction);
28 | }
29 |
30 | public void Free(ByteBuf buf)
31 | {
32 | buf.Clear();
33 | if (_bufs.Count < _maxCacheNum)
34 | {
35 | _bufs.Push(buf);
36 | }
37 | }
38 |
39 | public Stack GetByteBufsForTest()
40 | {
41 | return _bufs;
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/data/env/define.go:
--------------------------------------------------------------------------------
1 | package env
2 |
3 | import (
4 | "github.com/821869798/table-export/config"
5 | "github.com/821869798/table-export/data/enum"
6 | "github.com/821869798/table-export/field_type"
7 | )
8 |
9 | // 全局环境变量
10 | var (
11 | environment *Environment
12 | )
13 |
14 | // Environment 当前的环境
15 | type Environment struct {
16 | metaRuleUnitPlus config.MetaRuleUnitPlus
17 | // enumFiles 枚举文件
18 | enumFiles []*enum.DefineEnumFile
19 | // enumMapping 枚举映射
20 | enumMapping map[string]*enum.DefineEnum
21 | // extFieldMapping 扩展字段类型映射
22 | extFieldMapping map[string]field_type.IExtFieldType
23 | // extFieldClassFiles 生成自定义Class文件
24 | extFieldClassFiles map[string][]*field_type.TableFieldType
25 | }
26 |
27 | func InitEnv() {
28 | environment = &Environment{
29 | enumMapping: make(map[string]*enum.DefineEnum),
30 | extFieldMapping: make(map[string]field_type.IExtFieldType),
31 | extFieldClassFiles: make(map[string][]*field_type.TableFieldType),
32 | }
33 | }
34 |
35 | func SetMetaRuleUnitPlus(metaRuleUnitPlus config.MetaRuleUnitPlus) {
36 | environment.metaRuleUnitPlus = metaRuleUnitPlus
37 | }
38 |
39 | func GetMetaRuleUnitPlus() config.MetaRuleUnitPlus {
40 | return environment.metaRuleUnitPlus
41 | }
42 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/proj_cs_bin.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.5.33516.290
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "proj_cs_bin", "proj_cs_bin.csproj", "{5DB9D5C5-56A8-41B1-9296-F9E3B08682D4}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {5DB9D5C5-56A8-41B1-9296-F9E3B08682D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {5DB9D5C5-56A8-41B1-9296-F9E3B08682D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {5DB9D5C5-56A8-41B1-9296-F9E3B08682D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {5DB9D5C5-56A8-41B1-9296-F9E3B08682D4}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {3E2DAC04-95E9-4AA0-9174-1D3AB1359489}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/flag.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 | )
8 |
9 | type parameters struct {
10 | //生成模式
11 | mode string
12 | //额外参数
13 | extra string
14 | //source模板生成
15 | genSource string
16 | //自定义配置路径
17 | confFile string
18 | //使用帮助
19 | help bool
20 | //版本信息
21 | version bool
22 | }
23 |
24 | var params *parameters
25 |
26 | func init() {
27 |
28 | params = new(parameters)
29 | flag.StringVar(¶ms.mode, "m", "", `export data by source config , example [ -m lua ] or [ -m lua|json ] `)
30 | flag.StringVar(¶ms.extra, "e", "", `export data extra arg example `)
31 | flag.StringVar(¶ms.genSource, "g", "", `generator source template or , example [ -g base_test,base_test.xlsx,Sheet1 ]`)
32 | flag.BoolVar(¶ms.help, "help", false, "this help")
33 | flag.BoolVar(¶ms.version, "version", false, "this version")
34 | flag.StringVar(¶ms.confFile, "conf", "conf/config.toml", "use custom config.toml filepath,default is conf/config.toml")
35 | }
36 |
37 | func usage() {
38 | fmt.Fprintf(os.Stderr, `Usage: table-export [-m ] [-f ]
39 | `)
40 | flag.PrintDefaults()
41 | }
42 |
--------------------------------------------------------------------------------
/export/cs_bin/export_code_enum.go:
--------------------------------------------------------------------------------
1 | package cs_bin
2 |
3 | import (
4 | "github.com/821869798/fankit/fanstr"
5 | "github.com/821869798/table-export/config"
6 | "github.com/821869798/table-export/data/enum"
7 | "github.com/gookit/slog"
8 | "os"
9 | "path/filepath"
10 | "text/template"
11 | )
12 |
13 | func GenCSBinCodeEnum(enumFile *enum.DefineEnumFile, csBinRule *config.RawMetaRuleUnitCSBin, outputPath string) {
14 |
15 | templateRoot := &CSCodeWriteEnumFile{
16 | NameSpace: fanstr.ReplaceWindowsLineEnd(csBinRule.GenCodeNamespace),
17 | Enums: enumFile.Enums,
18 | EnumDefinePrefix: csBinRule.GetEnumDefinePrefix(),
19 | }
20 |
21 | //目标路径
22 | filePath := filepath.Join(outputPath, enumFile.FileName+".cs")
23 | file, err := os.Create(filePath)
24 | if err != nil {
25 | slog.Fatal(err)
26 | }
27 |
28 | //创建模板,绑定全局函数,并且解析
29 | tmpl, err := template.New("cs_bin_enum").Funcs(template.FuncMap{}).Parse(templateCSCodeEnum)
30 |
31 | //渲染输出
32 | err = tmpl.Execute(file, templateRoot)
33 | if err != nil {
34 | slog.Fatal(err)
35 | }
36 |
37 | err = file.Close()
38 | if err != nil {
39 | slog.Fatal(err)
40 | }
41 | }
42 |
43 | type CSCodeWriteEnumFile struct {
44 | NameSpace string
45 | EnumDefinePrefix string
46 | Enums []*enum.DefineEnum
47 | }
48 |
--------------------------------------------------------------------------------
/examples/cs_bin_config/scripts/common/strings.js:
--------------------------------------------------------------------------------
1 | const EmptyArray = [];
2 |
3 | const strings = {
4 | trimSplit(origin, sep) {
5 | origin = origin.trim();
6 | if (origin.length === 0) {
7 | return EmptyArray
8 | }
9 | return origin.split(sep);
10 | },
11 | trimSplitIntArray(origin, sep) {
12 | let array = this.trimSplit(origin, sep);
13 | for (let i = 0; i < array.length; i++) {
14 | array[i] = parseInt(array[i]);
15 | }
16 | return array;
17 | },
18 | trimSplitFloatArray(origin, sep) {
19 | let array = this.trimSplit(origin, sep);
20 | for (let i = 0; i < array.length; i++) {
21 | array[i] = parseFloat(array[i]);
22 | }
23 | return array;
24 | },
25 |
26 | stringToBool(str) {
27 | switch (str.toLowerCase().trim()) {
28 | case "true":
29 | case "1":
30 | return true;
31 | case "false":
32 | case "0":
33 | case "":
34 | return false;
35 | default:
36 | // 如果输入不是预期的字符串,可以抛出错误或返回null/undefined
37 | throw new Error("Invalid string for conversion to boolean");
38 | }
39 | }
40 |
41 | }
42 |
43 | module.exports = strings
--------------------------------------------------------------------------------
/data/env/env_enum.go:
--------------------------------------------------------------------------------
1 | package env
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/data/enum"
8 | )
9 |
10 | func AddEnumDefines(enumConfigs []*config.RawMetaEnumConfig) error {
11 | fileMapping := make(map[string]*enum.DefineEnumFile)
12 | for _, enumConfig := range enumConfigs {
13 | enumFile := enum.NewDefineEnumFile(enumConfig)
14 | for _, enumDefine := range enumFile.Enums {
15 | _, ok := environment.enumMapping[enumDefine.Name]
16 | if ok {
17 | return errors.New(fmt.Sprintf("enum name repeated: name[%v] ", enumDefine.Name))
18 | }
19 | if err := enumDefine.InitEnum(); err != nil {
20 | return err
21 | }
22 | environment.enumMapping[enumDefine.Name] = enumDefine
23 | }
24 | prevEnumFile, ok := fileMapping[enumFile.FileName]
25 | if ok {
26 | // 合并文件名定义相同的
27 | prevEnumFile.Enums = append(prevEnumFile.Enums, enumFile.Enums...)
28 | } else {
29 | environment.enumFiles = append(environment.enumFiles, enumFile)
30 | fileMapping[enumFile.FileName] = enumFile
31 | }
32 | }
33 | return nil
34 | }
35 |
36 | func GetEnumDefine(name string) (*enum.DefineEnum, bool) {
37 | v, ok := environment.enumMapping[name]
38 | return v, ok
39 | }
40 |
41 | func EnumFiles() []*enum.DefineEnumFile {
42 | return environment.enumFiles
43 | }
44 |
--------------------------------------------------------------------------------
/config/config_rule.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type RawMetaRule struct {
4 | RuleName string `toml:"rule_name"`
5 | ConfigDir string `toml:"config_dir"`
6 | Json *RawMetaRuleUnitJson `toml:"json"`
7 | Lua *RawMetaRuleUnitLua `toml:"lua"`
8 | CSBin *RawMetaRuleUnitCSBin `toml:"cs_bin"`
9 | RuleUnits []MetaRuleUnit
10 | }
11 |
12 | func (r *RawMetaRule) initMetaRule() {
13 | if r.Json != nil {
14 | r.RuleUnits = append(r.RuleUnits, r.Json)
15 | }
16 | if r.Lua != nil {
17 | r.RuleUnits = append(r.RuleUnits, r.Lua)
18 | }
19 | if r.CSBin != nil {
20 | r.RuleUnits = append(r.RuleUnits, r.CSBin)
21 | }
22 | }
23 |
24 | type RawMetaRuleUnitJson struct {
25 | JsonOutputDir string `toml:"json_out"`
26 | }
27 |
28 | func (r *RawMetaRuleUnitJson) RuleExportType() ExportType {
29 | return ExportType_Json
30 | }
31 |
32 | type RawMetaRuleUnitLua struct {
33 | LuaOutputDir string `toml:"lua_out"`
34 | EnableProcess bool `toml:"post_process"` //允许后处理
35 | TempDir string `toml:"temp_dir"`
36 | PostProcessLua string `toml:"post_process_lua"`
37 | PostWorkDir string `toml:"post_work_dir"`
38 | LuaWinDir string `toml:"lua_win_dir"`
39 | LuaMacDir string `toml:"lua_mac_dir"`
40 | }
41 |
42 | func (r *RawMetaRuleUnitLua) RuleExportType() ExportType {
43 | return ExportType_Lua
44 | }
45 |
--------------------------------------------------------------------------------
/data/source/source_excel.go:
--------------------------------------------------------------------------------
1 | package source
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/fankit/fanpath"
7 | "github.com/821869798/table-export/config"
8 | "github.com/821869798/table-export/data/model"
9 | "github.com/821869798/table-export/meta"
10 | "github.com/xuri/excelize/v2"
11 | )
12 |
13 | type DataSourceExcel struct {
14 | }
15 |
16 | func NewDataSourceExcel() IDataSource {
17 | d := &DataSourceExcel{}
18 | return d
19 | }
20 |
21 | func (d *DataSourceExcel) LoadDataModel(tableMetal *meta.TableMeta) (*model.TableModel, error) {
22 | //检测数量
23 | if len(tableMetal.Sources) == 0 {
24 | return nil, errors.New(fmt.Sprintf("table meta config[%v] sources count must >= 0", tableMetal.Target))
25 | }
26 |
27 | //读取excel数据到自定义结构体中
28 | dataModel := model.NewTableModel(tableMetal)
29 | for _, tableSource := range tableMetal.Sources {
30 | filePath := fanpath.RelExecuteDir(config.GlobalConfig.Table.SrcDir, tableSource.Table)
31 | excelFile, err := excelize.OpenFile(filePath)
32 | if err != nil {
33 | return nil, err
34 | }
35 | rowData, err := excelFile.GetRows(tableSource.Sheet)
36 | if err != nil {
37 | return nil, err
38 | }
39 | // 创建或者添加数据
40 | err = createOrAddDataModel(tableSource, dataModel, rowData)
41 | if err != nil {
42 | return nil, err
43 | }
44 | }
45 | return dataModel, nil
46 | }
47 |
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a golang project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
3 |
4 | name: Go
5 |
6 | on:
7 | workflow_dispatch:
8 | inputs:
9 | tag_name:
10 | description: 'tag name'
11 | required: true
12 |
13 | jobs:
14 |
15 | build:
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@v3
19 | - name: Set up Go
20 | uses: actions/setup-go@v3
21 | with:
22 | go-version: '1.21'
23 |
24 | - run: go build -o bin/${{ github.event.repository.name }}_linux_x64 .
25 | - run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o bin/${{ github.event.repository.name }}_win_x64.exe
26 | - run: CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -o bin/${{ github.event.repository.name }}_win_arm64.exe .
27 | - run: CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o bin/${{ github.event.repository.name }}_darwin_x64 .
28 | - run: CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o bin/${{ github.event.repository.name }}_darwin_arm64 .
29 |
30 | - name: Release
31 | uses: softprops/action-gh-release@v1
32 | with: # 将下述可执行文件 release 上去
33 | tag_name: ${{ github.event.inputs.tag_name }}
34 | files: bin/*
35 | draft: false
36 | env:
37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38 |
--------------------------------------------------------------------------------
/ext/ext_post/post_table_goja.go:
--------------------------------------------------------------------------------
1 | package ext_post
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/fankit/fanpath"
7 | "github.com/821869798/table-export/component/goja/table_engine"
8 | "github.com/821869798/table-export/data/model"
9 | "github.com/821869798/table-export/ext/apiext"
10 | "github.com/dop251/goja"
11 | "github.com/dop251/goja_nodejs/console"
12 | "github.com/dop251/goja_nodejs/require"
13 | )
14 |
15 | type ExtPostTableJS struct {
16 | ScriptPath string
17 | Error string
18 | jsFunc func(model *model.TableModel, context *ExtPostTableJS)
19 | }
20 |
21 | func NewExtPostTableJS(scriptPath string) (apiext.IExtPostTable, error) {
22 | e := &ExtPostTableJS{
23 | ScriptPath: scriptPath,
24 | }
25 |
26 | registry := require.NewRegistry(require.WithGlobalFolders(fanpath.ExecuteParentPath()))
27 |
28 | vm := goja.New()
29 | _ = registry.Enable(vm)
30 | console.Enable(vm)
31 | table_engine.Enable(vm)
32 |
33 | var script = fmt.Sprintf("const postTable = require(\"%s\");\npostTable;", e.ScriptPath)
34 |
35 | res, err := vm.RunString(script)
36 | if err != nil {
37 | return nil, err
38 | }
39 |
40 | if err := vm.ExportTo(res, &e.jsFunc); err != nil {
41 | return nil, err
42 | }
43 |
44 | return e, nil
45 | }
46 |
47 | func (e *ExtPostTableJS) PostTable(model *model.TableModel) error {
48 | e.Error = ""
49 | e.jsFunc(model, e)
50 | if e.Error != "" {
51 | return errors.New(e.Error)
52 | }
53 | return nil
54 | }
55 |
--------------------------------------------------------------------------------
/ext/ext_post/post_global_goja.go:
--------------------------------------------------------------------------------
1 | package ext_post
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/fankit/fanpath"
7 | "github.com/821869798/table-export/component/goja/table_engine"
8 | "github.com/821869798/table-export/data/model"
9 | "github.com/821869798/table-export/ext/apiext"
10 | "github.com/dop251/goja"
11 | "github.com/dop251/goja_nodejs/console"
12 | "github.com/dop251/goja_nodejs/require"
13 | )
14 |
15 | type ExtPostGlobalJS struct {
16 | ScriptPath string
17 | Error string
18 | jsFunc func(tableMap map[string]*model.TableModel, context *ExtPostGlobalJS)
19 | }
20 |
21 | func NewExtPostGlobalJS(scriptPath string) (apiext.IExtPostGlobal, error) {
22 | e := &ExtPostGlobalJS{
23 | ScriptPath: scriptPath,
24 | }
25 |
26 | registry := require.NewRegistry(require.WithGlobalFolders(fanpath.ExecuteParentPath()))
27 |
28 | vm := goja.New()
29 | _ = registry.Enable(vm)
30 | console.Enable(vm)
31 | table_engine.Enable(vm)
32 |
33 | var script = fmt.Sprintf("const postGlobal = require(\"%s\");\npostGlobal;", e.ScriptPath)
34 |
35 | res, err := vm.RunString(script)
36 | if err != nil {
37 | return nil, err
38 | }
39 |
40 | if err := vm.ExportTo(res, &e.jsFunc); err != nil {
41 | return nil, err
42 | }
43 |
44 | return e, nil
45 | }
46 |
47 | func (e *ExtPostGlobalJS) PostGlobal(tableMap map[string]*model.TableModel) error {
48 | e.Error = ""
49 | e.jsFunc(tableMap, e)
50 | if e.Error != "" {
51 | return errors.New(e.Error)
52 | }
53 | return nil
54 | }
55 |
--------------------------------------------------------------------------------
/examples/cs_bin_config/scripts/ext_field/field-vector3.js:
--------------------------------------------------------------------------------
1 | const {eFieldType} = require('../common/table-lib');
2 | const strings = require('../common/strings');
3 |
4 | const TypeName = "TestVector3";
5 |
6 | var ftc = tableEngine.NewTableFieldClass(TypeName);
7 | ftc.AddField("x", tableEngine.NewTableFieldType(eFieldType.Float));
8 | ftc.AddField("y", tableEngine.NewTableFieldType(eFieldType.Float));
9 | ftc.AddField("z", tableEngine.NewTableFieldType(eFieldType.Float));
10 |
11 | const fieldType = tableEngine.NewTableFieldClassType(ftc);
12 |
13 | const ArraySplit = tableEngine.TableConfig.ArraySplit;
14 |
15 | const extFieldVector3 = {
16 | Name() {
17 | return TypeName;
18 | },
19 | DefineFile() {
20 | return "CfgMath";
21 | },
22 | TableFieldType() {
23 | return fieldType;
24 | },
25 | ParseOriginData(origin, context) {
26 | const stringArray = strings.trimSplit(origin,ArraySplit);
27 | if (stringArray.length === 0) {
28 | return { x : 0, y: 0, z: 0};
29 | }
30 | if (stringArray.length !== 3) {
31 | context.Error = "Vector3 Parse Error,param count must be 3 or 0: " + origin;
32 | //console.error("js error:" + origin);
33 | return null
34 | }
35 | const x = parseFloat(stringArray[0]);
36 | const y = parseFloat(stringArray[1]);
37 | const z = parseFloat(stringArray[2]);
38 | return { x : x, y: y, z: z};
39 | }
40 | }
41 |
42 | module.exports = extFieldVector3;
--------------------------------------------------------------------------------
/convert/visitor/util.go:
--------------------------------------------------------------------------------
1 | package visitor
2 |
3 | import (
4 | "cmp"
5 | "github.com/821869798/table-export/field_type"
6 | "github.com/gookit/slog"
7 | "slices"
8 | )
9 |
10 | // GetMapSortedKeys 获取map的key的slice,并且排序
11 | func GetMapSortedKeys[T cmp.Ordered, V any](m map[T]V) []T {
12 | keys := make([]T, 0, len(m))
13 | for k := range m {
14 | keys = append(keys, k)
15 | }
16 | // 排序keys
17 | slices.Sort(keys)
18 | return keys
19 | }
20 |
21 | func GetMapSortedKeysInterface(m map[interface{}]interface{}, KeyType *field_type.TableFieldType) []interface{} {
22 | keys := make([]interface{}, 0, len(m))
23 | for k := range m {
24 | keys = append(keys, k)
25 | }
26 | // 排序keys
27 | slices.SortFunc(keys, func(a, b interface{}) int {
28 | switch value := a.(type) {
29 | case int32:
30 | return cmp.Compare(value, b.(int32))
31 | case float32:
32 | return cmp.Compare(value, b.(float32))
33 | case string:
34 | return cmp.Compare(value, b.(string))
35 | case uint32:
36 | return cmp.Compare(value, b.(uint32))
37 | case int64:
38 | return cmp.Compare(value, b.(int64))
39 | case float64:
40 | return cmp.Compare(value, b.(float64))
41 | case uint64:
42 | return cmp.Compare(value, b.(uint64))
43 | case int:
44 | return cmp.Compare(value, b.(int))
45 | case bool:
46 | if value == b.(bool) {
47 | return 0
48 | } else if value {
49 | return 1
50 | } else {
51 | return -1
52 | }
53 | default:
54 | slog.Fatalf("unsupported type: %T", value)
55 | return 0
56 | }
57 | })
58 | return keys
59 | }
60 |
--------------------------------------------------------------------------------
/data/model/tableoptimize.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "github.com/821869798/table-export/field_type"
5 | "github.com/821869798/table-export/meta"
6 | )
7 |
8 | type TableOptimize struct {
9 | OptimizeFields []*TableOptimizeField
10 | FieldsMap map[string]int
11 | }
12 |
13 | func NewTableOptimize() *TableOptimize {
14 | t := &TableOptimize{
15 | FieldsMap: make(map[string]int, 0),
16 | }
17 | return t
18 | }
19 |
20 | func (t *TableOptimize) AddOptimizeField(field *TableOptimizeField) {
21 | t.FieldsMap[field.Field.Target] = len(t.OptimizeFields)
22 | t.OptimizeFields = append(t.OptimizeFields, field)
23 | }
24 |
25 | func (t *TableOptimize) GetOptimizeField(field *meta.TableField) (*TableOptimizeField, int) {
26 | fIndex, ok := t.FieldsMap[field.Target]
27 | if !ok {
28 | return nil, -1
29 | }
30 |
31 | return t.OptimizeFields[fIndex], fIndex
32 | }
33 |
34 | type TableOptimizeField struct {
35 | Field *meta.TableField
36 | OptimizeDataInTableRow []int // 优化数据中在原始表的行数号
37 | DataUseIndex []int // 原始数据对应优化数据的索引
38 | OptimizeType *field_type.TableFieldType // 优化后的类型,就是包装了一层数组
39 | }
40 |
41 | func NewTableOptimizeField(field *meta.TableField, valueCount int, allCount int) *TableOptimizeField {
42 | t := &TableOptimizeField{
43 | Field: field,
44 | OptimizeDataInTableRow: make([]int, valueCount),
45 | DataUseIndex: make([]int, allCount),
46 | OptimizeType: field.Type.CreateArrayFieldType(),
47 | }
48 | return t
49 | }
50 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/821869798/table-export
2 |
3 | go 1.23.0
4 |
5 | toolchain go1.24.4
6 |
7 | require (
8 | github.com/821869798/fankit v0.0.10
9 | github.com/BurntSushi/toml v1.5.0
10 | github.com/bmatcuk/doublestar/v4 v4.9.0
11 | github.com/dop251/goja v0.0.0-20250630131328-58d95d85e994
12 | github.com/dop251/goja_nodejs v0.0.0-20250409162600-f7acab6894b0
13 | github.com/expr-lang/expr v1.17.5
14 | github.com/gookit/slog v0.5.8
15 | github.com/xuri/excelize/v2 v2.9.1
16 | )
17 |
18 | require (
19 | github.com/dlclark/regexp2 v1.11.5 // indirect
20 | github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
21 | github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect
22 | github.com/gookit/color v1.5.4 // indirect
23 | github.com/gookit/goutil v0.7.0 // indirect
24 | github.com/gookit/gsr v0.1.1 // indirect
25 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
26 | github.com/richardlehane/mscfb v1.0.4 // indirect
27 | github.com/richardlehane/msoleps v1.0.4 // indirect
28 | github.com/tiendc/go-deepcopy v1.6.1 // indirect
29 | github.com/valyala/bytebufferpool v1.0.0 // indirect
30 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
31 | github.com/xuri/efp v0.0.1 // indirect
32 | github.com/xuri/nfp v0.0.1 // indirect
33 | golang.org/x/crypto v0.40.0 // indirect
34 | golang.org/x/net v0.42.0 // indirect
35 | golang.org/x/sync v0.16.0 // indirect
36 | golang.org/x/sys v0.34.0 // indirect
37 | golang.org/x/term v0.33.0 // indirect
38 | golang.org/x/text v0.27.0 // indirect
39 | )
40 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/src/serialization/StringUtil.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 |
3 | namespace Serialization
4 | {
5 | public static class StringUtil
6 | {
7 | public static string ToStr(object o)
8 | {
9 | return ToStr(o, new StringBuilder());
10 | }
11 |
12 | public static string ToStr(object o, StringBuilder sb)
13 | {
14 | foreach (var p in o.GetType().GetFields())
15 | {
16 |
17 | sb.Append($"{p.Name} = {p.GetValue(o)},");
18 | }
19 |
20 | foreach (var p in o.GetType().GetProperties())
21 | {
22 | sb.Append($"{p.Name} = {p.GetValue(o)},");
23 | }
24 | return sb.ToString();
25 | }
26 |
27 | public static string ArrayToString(T[] arr)
28 | {
29 | return "[" + string.Join(",", arr) + "]";
30 | }
31 |
32 |
33 | public static string CollectionToString(IEnumerable arr)
34 | {
35 | return "[" + string.Join(",", arr) + "]";
36 | }
37 |
38 |
39 | public static string CollectionToString(IDictionary dic)
40 | {
41 | var sb = new StringBuilder('{');
42 | foreach (var e in dic)
43 | {
44 | sb.Append(e.Key).Append(':');
45 | sb.Append(e.Value).Append(',');
46 | }
47 | sb.Append('}');
48 | return sb.ToString();
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/examples/cs_bin_config/scripts/ext_table/game_config.js:
--------------------------------------------------------------------------------
1 | const {eFieldType} = require("../common/table-lib");
2 | const strings = require('../common/strings');
3 |
4 | const ArraySplit = tableEngine.TableConfig.ArraySplit;
5 |
6 | function PostTable(tableModel,context) {
7 | // 处理离散全局表的数据,解析成一个一个字段
8 | tableModel.ClearRecord = true
9 | const memTable = tableModel.MemTable
10 | const tableMeta = tableModel.Meta
11 | const rawDataMap = memTable.RawDataMapping()
12 | // 英雄等级上限
13 | const record1 = memTable.GetRecordByKey(1)
14 | tableMeta.AddExtraField("heroLevelMax",tableEngine.NewTableFieldType(eFieldType.Int),record1.remark)
15 | memTable.AddExtraData("heroLevelMax",parseInt(record1.data))
16 | // 初始队伍
17 | const record2 = memTable.GetRecordByKey(2)
18 | tableMeta.AddExtraField("heroInitTeam",tableEngine.NewTableFieldArrayType(tableEngine.NewTableFieldType(eFieldType.Int)),record2.remark)
19 | memTable.AddExtraData("heroInitTeam",strings.trimSplitIntArray(record2.data,ArraySplit))
20 | // 初始道具
21 | const record3 = memTable.GetRecordByKey(3)
22 | tableMeta.AddExtraField("initItems",tableEngine.GetExtFieldType("KVList_IntInt"),record3.remark)
23 | memTable.AddExtraData("initItems",tableEngine.ParseExtFieldValue("KVList_IntInt",record3.data))
24 | // 开启debug
25 | const record4 = memTable.GetRecordByKey(4)
26 | tableMeta.AddExtraField("openDebug",tableEngine.NewTableFieldType(eFieldType.Bool),record4.remark)
27 | memTable.AddExtraData("openDebug",strings.stringToBool(record4.data))
28 | }
29 |
30 | module.exports = PostTable;
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/src/gen/CfgCommon.cs:
--------------------------------------------------------------------------------
1 | using Serialization;
2 |
3 | using System.Collections.Generic;
4 |
5 | namespace CfgTable
6 | {
7 |
8 | public partial class KVList_IntInt
9 | {
10 | public int[] keys { get; private set; }
11 | public int[] values { get; private set; }
12 |
13 | public KVList_IntInt(ByteBuf _buf)
14 | {
15 | {int __n0 = _buf.ReadSize(); keys = new int[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ int __v0; __v0 = _buf.ReadInt(); keys[__i0] = __v0; } }
16 | {int __n0 = _buf.ReadSize(); values = new int[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ int __v0; __v0 = _buf.ReadInt(); values[__i0] = __v0; } }
17 | PostInit();
18 | }
19 |
20 | ///
21 | /// post process ext class
22 | ///
23 | partial void PostInit();
24 | }
25 |
26 | public partial class KVList_IntFloat
27 | {
28 | public int[] keys { get; private set; }
29 | public float[] values { get; private set; }
30 |
31 | public KVList_IntFloat(ByteBuf _buf)
32 | {
33 | {int __n0 = _buf.ReadSize(); keys = new int[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ int __v0; __v0 = _buf.ReadInt(); keys[__i0] = __v0; } }
34 | {int __n0 = _buf.ReadSize(); values = new float[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ float __v0; __v0 = _buf.ReadFloat(); values[__i0] = __v0; } }
35 | PostInit();
36 | }
37 |
38 | ///
39 | /// post process ext class
40 | ///
41 | partial void PostInit();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/821869798/fankit/fanpath"
5 | "github.com/BurntSushi/toml"
6 | "github.com/gookit/slog"
7 | )
8 |
9 | type RawGlobalConfig struct {
10 | Table *RawExcelTableConfig `toml:"table"`
11 | Meta *RawMetaConfig `toml:"meta"`
12 | }
13 |
14 | type RawExcelTableConfig struct {
15 | SrcDir string `toml:"src_dir"`
16 | Name int `toml:"name"`
17 | Desc int `toml:"desc"`
18 | DataStart int `toml:"data_start"`
19 | ArraySplit string `toml:"array_split"`
20 | MapSplit1 string `toml:"map_split1"`
21 | MapSplit2 string `toml:"map_split2"`
22 | }
23 |
24 | type RawMetaConfig struct {
25 | GenDir string `toml:"gen_dir"`
26 | Rules []*RawMetaRule `toml:"rules"`
27 | RuleMap map[string]*RawMetaRule
28 | }
29 |
30 | func GetMetaRuleConfigByName(name string) *RawMetaRule {
31 | rule, ok := GlobalConfig.Meta.RuleMap[name]
32 | if ok {
33 | return rule
34 | }
35 | return nil
36 | }
37 |
38 | var GlobalConfig *RawGlobalConfig
39 |
40 | func ParseConfig(configFile string) {
41 | configFile = fanpath.AbsOrRelExecutePath(configFile)
42 | GlobalConfig = new(RawGlobalConfig)
43 | if _, err := toml.DecodeFile(configFile, GlobalConfig); err != nil {
44 | slog.Fatalf("load global config error:%v", err)
45 | }
46 |
47 | //初始化
48 | GlobalConfig.Meta.RuleMap = make(map[string]*RawMetaRule, 4)
49 | for _, rule := range GlobalConfig.Meta.Rules {
50 | rule.initMetaRule()
51 | GlobalConfig.Meta.RuleMap[rule.RuleName] = rule
52 | }
53 |
54 | slog.Debug("load global config success!")
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/convert/apiconvert/icodeprinter.go:
--------------------------------------------------------------------------------
1 | package apiconvert
2 |
3 | import (
4 | "github.com/821869798/table-export/field_type"
5 | )
6 |
7 | type ICodePrinter interface {
8 | AcceptField(fieldType *field_type.TableFieldType, fieldName string, reader string) string
9 | AcceptOptimizeAssignment(fieldName string, reader string, commonDataName string) string
10 | AcceptInt(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
11 | AcceptUInt(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
12 | AcceptLong(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
13 | AcceptULong(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
14 | AcceptFloat(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
15 | AcceptDouble(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
16 | AcceptBool(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
17 | AcceptString(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
18 | AcceptEnum(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
19 | AcceptArray(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
20 | AcceptMap(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
21 | AcceptClass(fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string
22 | }
23 |
--------------------------------------------------------------------------------
/data/source/source_csv.go:
--------------------------------------------------------------------------------
1 | package source
2 |
3 | import (
4 | "encoding/csv"
5 | "errors"
6 | "fmt"
7 | "github.com/821869798/fankit/fanpath"
8 | "github.com/821869798/table-export/config"
9 | "github.com/821869798/table-export/data/model"
10 | "github.com/821869798/table-export/meta"
11 | "os"
12 | "strings"
13 | )
14 |
15 | type DataSourceCsv struct {
16 | }
17 |
18 | func NewDataSourceCsv() IDataSource {
19 | d := &DataSourceCsv{}
20 | return d
21 | }
22 |
23 | func (d *DataSourceCsv) LoadDataModel(tableMetal *meta.TableMeta) (*model.TableModel, error) {
24 | //检测数量
25 | if len(tableMetal.Sources) == 0 {
26 | return nil, errors.New(fmt.Sprintf("table meta config[%v] sources count must >= 0", tableMetal.Target))
27 | }
28 |
29 | //读取excel数据到自定义结构体中
30 | dataModel := model.NewTableModel(tableMetal)
31 | for _, tableSource := range tableMetal.Sources {
32 | filePath := fanpath.RelExecuteDir(config.GlobalConfig.Table.SrcDir, tableSource.Table)
33 |
34 | csvFile, err := os.Open(filePath)
35 | if err != nil {
36 | return nil, err
37 | }
38 | csvReader := csv.NewReader(csvFile) //创建一个新的写入文件流
39 | csvReader.LazyQuotes = true
40 | rowData, err := csvReader.ReadAll()
41 |
42 | if err != nil {
43 | return nil, err
44 | }
45 |
46 | err = csvFile.Close()
47 | if err != nil {
48 | return nil, err
49 | }
50 |
51 | //去除utf-8的bom
52 | if len(rowData) > 0 && len(rowData[0]) > 0 {
53 | firstData := rowData[0][0]
54 | rowData[0][0] = strings.TrimPrefix(firstData, "\uFEFF")
55 | }
56 |
57 | // 创建或者添加数据
58 | err = createOrAddDataModel(tableSource, dataModel, rowData)
59 | if err != nil {
60 | return nil, err
61 | }
62 | }
63 | return dataModel, nil
64 | }
65 |
--------------------------------------------------------------------------------
/config/config_rule_csbin.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type RawMetaRuleUnitCSBin struct {
4 | CodeTempDir string `toml:"code_temp_dir"`
5 | DataTempDir string `toml:"data_temp_dir"`
6 | GenCodeDir string `toml:"gen_code_dir"`
7 | DataBinDir string `toml:"data_bin_dir"`
8 | GenCodeNamespace string `toml:"code_namespace"`
9 | GenCodeHead string `toml:"gen_code_head"`
10 | CodeNotFoundKey string `toml:"code_not_found_key"`
11 | GenOptimizeData bool `toml:"gen_optimize"`
12 | EnumFiles []string `toml:"enum_files"`
13 | EnumDefinePrefix string `toml:"enum_define_prefix"`
14 | ClassDefinePrefix string `toml:"class_define_prefix"`
15 | BuiltinFieldTypes []string `toml:"builtin_field_types"`
16 | ScriptFieldTypes []string `toml:"script_field_types"`
17 | PostGlobalScript string `toml:"post_global_script"`
18 | }
19 |
20 | func (r *RawMetaRuleUnitCSBin) RuleExportType() ExportType {
21 | return ExportType_CS_Bin
22 | }
23 |
24 | func (r *RawMetaRuleUnitCSBin) ActiveOptimizeData() bool {
25 | return r.GenOptimizeData
26 | }
27 |
28 | func (r *RawMetaRuleUnitCSBin) GetEnumFiles() []string {
29 | return r.EnumFiles
30 | }
31 |
32 | func (r *RawMetaRuleUnitCSBin) GetEnumDefinePrefix() string {
33 | return r.EnumDefinePrefix
34 | }
35 |
36 | func (r *RawMetaRuleUnitCSBin) GetClassDefinePrefix() string {
37 | return r.ClassDefinePrefix
38 | }
39 |
40 | func (r *RawMetaRuleUnitCSBin) GetBuiltinFieldTypes() []string {
41 | return r.BuiltinFieldTypes
42 | }
43 |
44 | func (r *RawMetaRuleUnitCSBin) GetExtFieldTypeScriptPath() []string {
45 | return r.ScriptFieldTypes
46 | }
47 |
48 | func (r *RawMetaRuleUnitCSBin) GetPostGlobalScriptPath() string {
49 | return r.PostGlobalScript
50 | }
51 |
--------------------------------------------------------------------------------
/data/check/check_entry.go:
--------------------------------------------------------------------------------
1 | package check
2 |
3 | import (
4 | "github.com/821869798/table-export/data/model"
5 | "github.com/gookit/slog"
6 | "os"
7 | )
8 |
9 | func Run(dataMode *model.TableModel, global map[string]map[interface{}]interface{}) {
10 | if dataMode.MemTable == nil {
11 | return
12 | }
13 | meta := dataMode.Meta
14 | for _, tck := range meta.RecordChecks {
15 | err := tck.CompileCode(dataMode.MemTable.RawDataMapping(), global, dataMode.MemTable.GetExtraDataMap())
16 | if err != nil {
17 | slog.Fatalf("table[%s] check compile code[%s] error:%v", meta.Target, tck.Code, err)
18 | os.Exit(1)
19 | }
20 | }
21 |
22 | // 检查每条数据
23 | for _, record := range dataMode.MemTable.RawDataList() {
24 | for _, tck := range meta.RecordChecks {
25 | checkResult, err := tck.Run(record)
26 | if err != nil {
27 | slog.Fatalf("table[%s] check run code[%s] error:%v", meta.Target, tck.Code, err)
28 | os.Exit(1)
29 | }
30 | if !checkResult {
31 | slog.Fatalf("table[%s] check run code[%s] check faield:%v", meta.Target, tck.Code, record)
32 | os.Exit(1)
33 | }
34 | }
35 | }
36 |
37 | // 检查单表的全局
38 | for _, tck := range meta.GlobalChecks {
39 | err := tck.CompileCode(dataMode.MemTable.RawDataMapping(), global, dataMode.MemTable.GetExtraDataMap())
40 | if err != nil {
41 | slog.Fatalf("table[%s] check compile global code[%s] error:%v", meta.Target, tck.Code, err)
42 | os.Exit(1)
43 | }
44 | checkResult, err := tck.RunGlobal()
45 | if err != nil {
46 | slog.Fatalf("table[%s] check run global code[%s] error:%v", meta.Target, tck.Code, err)
47 | os.Exit(1)
48 | }
49 | if !checkResult {
50 | slog.Fatalf("table[%s] check run global code[%s] check faield", meta.Target, tck.Code)
51 | os.Exit(1)
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/examples/cs_bin_config/table_meta/base_test.toml:
--------------------------------------------------------------------------------
1 |
2 | # 目标名字
3 | target = "base_test"
4 | mode = ""
5 | source_type = "excel"
6 |
7 | sources = [
8 | { file_name = "base_test.xlsx", sheet_name = "Sheet1" },
9 | ]
10 |
11 |
12 | fields = [
13 | { active = true, sname = "id" , tname = "id" , type = "int" , key = 1, desc = "主id"},
14 | { active = true, sname = "name" , tname = "name" , type = "string", key = 0, desc = "名字"},
15 | { active = true, sname = "age" , tname = "age" , type = "int" , key = 0, desc = "年龄"},
16 | { active = true, sname = "course" , tname = "course" , type = "[]int" , key = 0, desc = "学科"},
17 | { active = true, sname = "big_data_id" , tname = "bigDataId" , type = "int" , key = 0, desc = "对应big_data表id"},
18 | { active = true, sname = "item_type" , tname = "itemType" , type = "ItemType" , key = 0, desc = "道具类型,测试枚举1"},
19 | { active = true, sname = "condition_type" , tname = "conditionType" , type = "ConditionType" , key = 0, desc = "条件类型,测试枚举2"},
20 | { active = true, sname = "position", tname = "position", type = "PointInt", key = 0, desc = "位置"},
21 | { active = true, sname = "rewards", tname = "rewards", type = "KVList_IntInt", key = 0, desc = "奖励"},
22 | { active = true, sname = "vector3", tname = "vector3", type = "TestVector3", key = 0, desc = "测试使用脚本自定义字段类型" }
23 | ]
24 |
25 | # 数据检测示例,$代表一行数据,table代表当前表,global代表全局,global[name]可以取到其他表的数据,extra表示当前表的额外新增数据
26 | # global为false表是一行行数据检测,会有$。如果通过&&放到一行配置中能提升运行效率
27 | # global为true表示是这个表单次的检查
28 | checks = [
29 | { active = true, global = false, code = """ $.age < 60 """ },
30 | { active = true, global = false, code = """ $.bigDataId == 0 || global["big_data"][$.bigDataId] != nil """ },
31 | { active = true, global = true, code = """ table[int32(4)].name =="李四" """ },
32 | ]
33 |
--------------------------------------------------------------------------------
/ext/ext_field/point_int.go:
--------------------------------------------------------------------------------
1 | package ext_field
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/fankit/fanstr"
7 | "github.com/821869798/table-export/config"
8 | "github.com/821869798/table-export/field_type"
9 | "strconv"
10 | )
11 |
12 | // ExtFieldPointInt 示例,使用go扩展一个字段类型
13 | type ExtFieldPointInt struct {
14 | FieldType *field_type.TableFieldType
15 | }
16 |
17 | func NewExtFieldPointInt() field_type.IExtFieldType {
18 | ft := field_type.NewTableFieldClass("PointInt")
19 | ft.AddField("x", field_type.NewTableFieldType(field_type.EFieldType_Int))
20 | ft.AddField("y", field_type.NewTableFieldType(field_type.EFieldType_Int))
21 |
22 | e := &ExtFieldPointInt{
23 | FieldType: field_type.NewTableFieldClassType(ft),
24 | }
25 |
26 | e.FieldType.SetExtFieldType(e)
27 |
28 | return e
29 | }
30 |
31 | func (e *ExtFieldPointInt) Name() string {
32 | return e.FieldType.Name
33 | }
34 |
35 | func (e *ExtFieldPointInt) DefineFile() string {
36 | return "CfgMath"
37 | }
38 |
39 | func (e *ExtFieldPointInt) TableFieldType() *field_type.TableFieldType {
40 | return e.FieldType
41 | }
42 |
43 | func (e *ExtFieldPointInt) ParseOriginData(origin string) (interface{}, error) {
44 | strSlice := fanstr.SplitEx(origin, config.GlobalConfig.Table.ArraySplit)
45 | if len(strSlice) == 0 {
46 | return map[string]interface{}{
47 | "x": int32(0),
48 | "y": int32(0),
49 | }, nil
50 | }
51 | if len(strSlice) != 2 {
52 | return nil, errors.New(fmt.Sprintf("PointInt ParseOriginData error,need array length 2 or 0: [%v]", origin))
53 | }
54 |
55 | x, err := strconv.Atoi(strSlice[0])
56 | if err != nil {
57 | return nil, errors.New(fmt.Sprintf("PointInt ParseOriginData error,need param x int: [%v]", origin))
58 | }
59 | y, err := strconv.Atoi(strSlice[1])
60 | if err != nil {
61 | return nil, errors.New(fmt.Sprintf("PointInt ParseOriginData error,need param y int: [%v]", origin))
62 | }
63 | return map[string]interface{}{
64 | "x": int32(x),
65 | "y": int32(y),
66 | }, nil
67 | }
68 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/Program.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 | using Serialization;
3 | using System.Text.Json;
4 |
5 |
6 | [MemoryDiagnoser]
7 | public class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 |
12 | var bytes = File.ReadAllBytes("table_bytes/big_data.bytes");
13 | ByteBuf buf = new ByteBuf(bytes);
14 | var table = new CfgTable.Tblbig_data(buf);
15 | var cfg1 = table.Get(1);
16 | Console.WriteLine(JsonSerializer.Serialize(cfg1));
17 | Console.WriteLine(table.DataCount);
18 |
19 | bytes = File.ReadAllBytes("table_bytes/base_test.bytes");
20 | ByteBuf buf2 = new ByteBuf(bytes);
21 | var table2 = new CfgTable.Tblbase_test(buf2);
22 | var cfg2 = table2.Get(2);
23 | Console.WriteLine(JsonSerializer.Serialize(cfg2));
24 |
25 | bytes = File.ReadAllBytes("table_bytes/complex_test.bytes");
26 | ByteBuf buf3 = new ByteBuf(bytes);
27 | var table3 = new CfgTable.Tblcomplex_test(buf3);
28 | var dataRecord = table3.Get(1, "k3");
29 | if (dataRecord != null)
30 | {
31 | Console.WriteLine(dataRecord.content);
32 | }
33 |
34 | bytes = File.ReadAllBytes($"table_bytes/{CfgTable.TblGameConfig.TableFileName}.bytes");
35 | ByteBuf buf4 = new ByteBuf(bytes);
36 | var table4 = new CfgTable.TblGameConfig(buf4);
37 | Console.WriteLine(JsonSerializer.Serialize(table4));
38 |
39 | //var summary = BenchmarkRunner.Run();
40 |
41 | }
42 |
43 |
44 | byte[] bytes;
45 | [GlobalSetup]
46 | public void Setup()
47 | {
48 | bytes = File.ReadAllBytes("D:\\program\\go\\table-export\\examples\\projects\\proj_cs_bin\\table_bytes\\big_data.bytes");
49 | }
50 |
51 | [Benchmark]
52 | public void TestTableLoad()
53 | {
54 | ByteBuf buf = new ByteBuf(bytes);
55 | var table = new CfgTable.Tblbig_data(buf);
56 | int count = table.DataCount;
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/export/entry.go:
--------------------------------------------------------------------------------
1 | package export
2 |
3 | import (
4 | "github.com/821869798/fankit/fanpath"
5 | "github.com/821869798/table-export/config"
6 | "github.com/821869798/table-export/data/env"
7 | "github.com/821869798/table-export/meta"
8 | "github.com/gookit/slog"
9 | "strings"
10 | "sync"
11 | )
12 |
13 | type Entry struct {
14 | mode string //转换模式
15 | extra string //额外参数
16 | }
17 |
18 | func NewEntry(mode, extra string) *Entry {
19 | e := &Entry{
20 | mode: mode,
21 | extra: extra,
22 | }
23 | return e
24 | }
25 |
26 | func (e *Entry) Run() {
27 | extraArg := make(map[string]string)
28 | strMap1 := strings.Split(e.extra, "|")
29 | for _, v := range strMap1 {
30 | if v == "" {
31 | continue
32 | }
33 | kvStr := strings.Split(v, "=")
34 | if len(kvStr) == 2 {
35 | extraArg[kvStr[0]] = kvStr[1]
36 | } else if len(kvStr) == 1 {
37 | extraArg[kvStr[0]] = ""
38 | }
39 | }
40 |
41 | wg := sync.WaitGroup{}
42 |
43 | modeSlice := strings.Split(e.mode, "|")
44 | for _, mode := range modeSlice {
45 | metaRule := config.GetMetaRuleConfigByName(mode)
46 | if metaRule == nil {
47 | slog.Fatalf("export mode can't not find in config:%v", mode)
48 | }
49 |
50 | tableMetas, err := meta.LoadTableMetasByDir(fanpath.RelExecuteDir(metaRule.ConfigDir))
51 | if err != nil {
52 | slog.Fatalf("load table meta toml config failed! mode:%s err:%v", mode, err)
53 | }
54 |
55 | for _, rule := range metaRule.RuleUnits {
56 | if creatorFunc, ok := exportCreators[rule.RuleExportType()]; ok {
57 | slog.Debugf("start run export mode:%s", mode)
58 |
59 | export := creatorFunc(tableMetas, extraArg)
60 | //因为有个全局的环境,所以不支持同时转换多个mode,需要依次执行
61 | env.InitEnv()
62 | if rulePlus, ok := rule.(config.MetaRuleUnitPlus); ok {
63 | env.SetMetaRuleUnitPlus(rulePlus)
64 | }
65 | export.Export(rule)
66 | //wg.Add(1)
67 | //go func() {
68 | // export.Export(rule)
69 | // wg.Done()
70 | //}()
71 | } else {
72 | slog.Fatalf("export mode can't support:%v", rule.RuleExportType())
73 | }
74 | }
75 |
76 | }
77 |
78 | wg.Wait()
79 | }
80 |
--------------------------------------------------------------------------------
/ext/ext_field/kvlist_int_float.go:
--------------------------------------------------------------------------------
1 | package ext_field
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/fankit/fanstr"
7 | "github.com/821869798/table-export/config"
8 | "github.com/821869798/table-export/field_type"
9 | "strconv"
10 | )
11 |
12 | type ExtFieldKVListIntFloat struct {
13 | ExtFieldKVListIntInt
14 | }
15 |
16 | func NewExtFieldKVListIntFloat() field_type.IExtFieldType {
17 | ft := field_type.NewTableFieldClass("KVList_IntFloat")
18 | ft.AddField("keys", field_type.NewTableFieldArrayType(field_type.NewTableFieldType(field_type.EFieldType_Int)))
19 | ft.AddField("values", field_type.NewTableFieldArrayType(field_type.NewTableFieldType(field_type.EFieldType_Float)))
20 |
21 | e := &ExtFieldKVListIntFloat{}
22 | e.FieldType = field_type.NewTableFieldClassType(ft)
23 | e.FieldType.SetExtFieldType(e)
24 |
25 | return e
26 | }
27 |
28 | func (e *ExtFieldKVListIntFloat) ParseOriginData(origin string) (interface{}, error) {
29 | strSlice := fanstr.SplitEx(origin, config.GlobalConfig.Table.MapSplit1)
30 | if len(strSlice) == 0 {
31 | return map[string]interface{}{
32 | "keys": []interface{}{},
33 | "values": []interface{}{},
34 | }, nil
35 | }
36 |
37 | keys := make([]interface{}, 0, len(strSlice))
38 | values := make([]interface{}, 0, len(strSlice))
39 | for _, v := range strSlice {
40 | strSlice := fanstr.SplitEx(v, config.GlobalConfig.Table.MapSplit2)
41 | if len(strSlice) == 0 {
42 | continue
43 | }
44 | if len(strSlice) != 2 {
45 | return nil, errors.New(fmt.Sprintf("KVListIntFloat ParseOriginData error,need array length 2: [%v]", v))
46 | }
47 | k, err := strconv.Atoi(strSlice[0])
48 | if err != nil {
49 | return nil, errors.New(fmt.Sprintf("KVListIntFloat ParseOriginData error,need param left int: [%v]", v))
50 | }
51 | v, err := strconv.ParseFloat(strSlice[1], 32)
52 | if err != nil {
53 | return nil, errors.New(fmt.Sprintf("KVListIntFloat ParseOriginData error,need param right float: [%v]", v))
54 | }
55 | keys = append(keys, int32(k))
56 | values = append(values, float32(v))
57 | }
58 |
59 | return map[string]interface{}{
60 | "keys": keys,
61 | "values": values,
62 | }, nil
63 | }
64 |
--------------------------------------------------------------------------------
/data/enum/enum.go:
--------------------------------------------------------------------------------
1 | package enum
2 |
3 | import (
4 | "cmp"
5 | "errors"
6 | "fmt"
7 | "github.com/821869798/table-export/config"
8 | "slices"
9 | )
10 |
11 | type DefineEnumFile struct {
12 | FileName string
13 | Enums []*DefineEnum
14 | }
15 |
16 | type DefineEnum struct {
17 | Name string
18 | Parse4String bool
19 | Values []*config.RawMetaEnumValue
20 | ValueMapping map[int32]*config.RawMetaEnumValue
21 | ValueStrMapping map[string]*config.RawMetaEnumValue
22 | }
23 |
24 | func NewNewDefineEnum(enumDefine *config.RawMetaEnumDefine) *DefineEnum {
25 | e := &DefineEnum{
26 | Name: enumDefine.Name,
27 | Parse4String: enumDefine.Parse4String,
28 | Values: enumDefine.Values,
29 | ValueMapping: make(map[int32]*config.RawMetaEnumValue, len(enumDefine.Values)),
30 | }
31 | if e.Parse4String {
32 | e.ValueStrMapping = make(map[string]*config.RawMetaEnumValue, len(enumDefine.Values))
33 | }
34 | return e
35 | }
36 |
37 | func (d *DefineEnum) InitEnum() error {
38 | for _, enumValue := range d.Values {
39 | _, ok := d.ValueMapping[enumValue.Index]
40 | if ok {
41 | return errors.New(fmt.Sprintf("enum value repeated: name[%s] index[%d]", d.Name, enumValue.Index))
42 | }
43 | d.ValueMapping[enumValue.Index] = enumValue
44 | if d.Parse4String {
45 | _, ok = d.ValueStrMapping[enumValue.ValueString]
46 | if ok {
47 | return errors.New(fmt.Sprintf("enum value repeated: name[%s] valueString[%s]", d.Name, enumValue.ValueString))
48 | }
49 | d.ValueStrMapping[enumValue.ValueString] = enumValue
50 | }
51 | }
52 |
53 | _, ok := d.ValueMapping[0]
54 | if !ok {
55 | return errors.New(fmt.Sprintf("enum value must hava index 0: name[%s]", d.Name))
56 | }
57 |
58 | // 排序
59 | slices.SortFunc(d.Values, func(a, b *config.RawMetaEnumValue) int {
60 | return cmp.Compare(a.Index, b.Index)
61 | })
62 | return nil
63 | }
64 |
65 | func NewDefineEnumFile(enumConfig *config.RawMetaEnumConfig) *DefineEnumFile {
66 | enumFile := &DefineEnumFile{
67 | Enums: make([]*DefineEnum, 0, len(enumConfig.EnumDefines)),
68 | FileName: enumConfig.EnumFileName,
69 | }
70 | for _, enumDefine := range enumConfig.EnumDefines {
71 | enumFile.Enums = append(enumFile.Enums, NewNewDefineEnum(enumDefine))
72 | }
73 | return enumFile
74 | }
75 |
--------------------------------------------------------------------------------
/ext/ext_field_script/field_goja.go:
--------------------------------------------------------------------------------
1 | package ext_field_script
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/fankit/fanpath"
7 | "github.com/821869798/table-export/component/goja/table_engine"
8 | "github.com/821869798/table-export/field_type"
9 | "github.com/dop251/goja"
10 | "github.com/dop251/goja_nodejs/console"
11 | "github.com/dop251/goja_nodejs/require"
12 | "sync"
13 | )
14 |
15 | // ExtFieldJS 通过js脚本扩展字段类型
16 | type ExtFieldJS struct {
17 | FieldType *field_type.TableFieldType
18 | ScriptPath string
19 | jsResult *jsScriptResult
20 | Error string
21 | mutex sync.Mutex
22 | }
23 |
24 | type jsScriptResult struct {
25 | Name string
26 | DefineFile string
27 | ParseFunc func(string, *ExtFieldJS) interface{}
28 | TableFieldType func() *field_type.TableFieldType
29 | }
30 |
31 | func NewExtFieldJS(scripPath string) (field_type.IExtFieldType, error) {
32 |
33 | e := &ExtFieldJS{
34 | ScriptPath: scripPath,
35 | jsResult: &jsScriptResult{},
36 | }
37 | registry := require.NewRegistry(require.WithGlobalFolders(fanpath.ExecuteParentPath()))
38 |
39 | vm := goja.New()
40 | _ = registry.Enable(vm)
41 | console.Enable(vm)
42 | table_engine.Enable(vm)
43 |
44 | var script = fmt.Sprintf("const extField = require(\"%s\");\n", e.ScriptPath) +
45 | `const result = { Name: extField.Name(),DefineFile: extField.DefineFile(),TableFieldType: extField.TableFieldType, ParseFunc: extField.ParseOriginData};
46 | result;`
47 | res, err := vm.RunString(script)
48 | if err != nil {
49 | return nil, err
50 | }
51 |
52 | if err := vm.ExportTo(res, e.jsResult); err != nil {
53 | return nil, err
54 | }
55 |
56 | e.FieldType = e.jsResult.TableFieldType()
57 | e.FieldType.SetExtFieldType(e)
58 |
59 | return e, nil
60 | }
61 |
62 | func (e *ExtFieldJS) Name() string {
63 | return e.jsResult.Name
64 | }
65 |
66 | func (e *ExtFieldJS) DefineFile() string {
67 | return e.jsResult.DefineFile
68 | }
69 |
70 | func (e *ExtFieldJS) TableFieldType() *field_type.TableFieldType {
71 | return e.FieldType
72 | }
73 |
74 | func (e *ExtFieldJS) ParseOriginData(origin string) (interface{}, error) {
75 | // goja 不支持线程安全
76 | e.mutex.Lock()
77 | defer e.mutex.Unlock()
78 |
79 | e.Error = ""
80 | result := e.jsResult.ParseFunc(origin, e)
81 | if e.Error != "" {
82 | return nil, errors.New(e.Error)
83 | }
84 | return result, nil
85 | }
86 |
--------------------------------------------------------------------------------
/data/source/util.go:
--------------------------------------------------------------------------------
1 | package source
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/data/model"
8 | "github.com/821869798/table-export/meta"
9 | )
10 |
11 | func createOrAddDataModel(tableSource *meta.TableSource, dataModel *model.TableModel, rowData [][]string) error {
12 | //数据行数不够
13 | if len(rowData) < config.GlobalConfig.Table.DataStart {
14 | return errors.New(fmt.Sprintf("excel source file[%v] sheet[%v] row count must >= %v", tableSource.Table, tableSource.Sheet, config.GlobalConfig.Table.DataStart))
15 | }
16 |
17 | //名字行,先存成一个map
18 | nameRow := rowData[config.GlobalConfig.Table.Name]
19 | nameIndex := make(map[string]int)
20 | for index, name := range nameRow {
21 | //先判断是否需要这个字段
22 | if _, ok := dataModel.Meta.SourceMap[name]; !ok {
23 | continue
24 | }
25 | //excel中需要的字段重复了
26 | if _, ok := nameIndex[name]; ok {
27 | return errors.New(fmt.Sprintf("excel source file[%v] sheet[%v] name repeated[%v]", tableSource.Table, tableSource.Sheet))
28 | }
29 | nameIndex[name] = index
30 | }
31 |
32 | if dataModel.NameIndexMapping == nil {
33 | dataModel.NameIndexMapping = make(map[string]int)
34 | //只有一个的情况
35 | for _, tf := range dataModel.Meta.Fields {
36 | sourceIndex, ok := nameIndex[tf.Source]
37 | if !ok {
38 | return errors.New(fmt.Sprintf("excel source file[%v] sheet[%v] can't find field name[%v]", tableSource.Table, tableSource.Sheet, tf.Source))
39 | }
40 | dataModel.NameIndexMapping[tf.Target] = sourceIndex
41 | }
42 | dataModel.NameRow = nameRow
43 | dataModel.DescRow = rowData[config.GlobalConfig.Table.Desc]
44 | dataModel.RawData = rowData[config.GlobalConfig.Table.DataStart:]
45 | } else {
46 | //检验多分配置的索引以及顺序是否一致
47 | for _, tf := range dataModel.Meta.Fields {
48 | sourceIndex, ok := nameIndex[tf.Source]
49 | if !ok {
50 | return errors.New(fmt.Sprintf("excel source file[%v] sheet[%v] can't find field name[%v]", tableSource.Table, tableSource.Sheet, tf.Source))
51 | }
52 | oldIndex, ok := dataModel.NameIndexMapping[tf.Target]
53 | if sourceIndex != oldIndex {
54 | return errors.New(fmt.Sprintf("excel multiply sources field name not inconformity,target:%v", dataModel.Meta.Target))
55 | }
56 | }
57 | dataModel.RawData = append(dataModel.RawData, rowData[config.GlobalConfig.Table.DataStart:]...)
58 | }
59 |
60 | return nil
61 | }
62 |
--------------------------------------------------------------------------------
/convert/wrap/wrap_string.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/field_type"
9 | "strings"
10 | )
11 |
12 | type stringWrap struct{}
13 |
14 | func (b *stringWrap) OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
15 | return origin, nil
16 | }
17 |
18 | func (b *stringWrap) OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
19 | switch exportType {
20 | case config.ExportType_Lua:
21 | newValue := strings.Replace(origin, "\\", "\\\\", -1)
22 | newValue = strings.Replace(newValue, "\n", "\\n", -1)
23 | newValue = strings.Replace(newValue, "\"", "\\\"", -1)
24 | newValue = "\"" + newValue + "\""
25 | return newValue, nil
26 | default:
27 | return origin, nil
28 | }
29 | }
30 |
31 | func (b *stringWrap) OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
32 | switch exportType {
33 | case config.ExportType_CS_Bin:
34 | return "string", nil
35 | default:
36 | return "", errors.New("no support export Type Output DefType")
37 | }
38 | }
39 |
40 | func (b *stringWrap) FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
41 | _, ok := origin.(string)
42 | if ok {
43 | return origin, nil
44 | }
45 | return nil, errors.New(fmt.Sprintf("[FormatValueInterface|string] no support type[%T]", origin))
46 | }
47 |
48 | func (b *stringWrap) DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
49 | visitor.AcceptString(origin)
50 | return nil
51 | }
52 |
53 | func (b *stringWrap) DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
54 | stringValue, ok := origin.(string)
55 | if ok {
56 | visitor.AcceptString(stringValue)
57 | return nil
58 | }
59 | return errors.New(fmt.Sprintf("[DataVisitorValue|string] no support type[%T]", origin))
60 | }
61 |
62 | func (b *stringWrap) CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
63 | return print.AcceptString(fieldType, fieldName, reader, depth)
64 | }
65 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/src/serialization/ThreadLocalTemporalByteBufPool.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Serialization
3 | {
4 |
5 |
6 | public class ThreadLocalTemporalByteBufPool
7 | {
8 | [ThreadStatic]
9 | private static ByteBufPool s_pool;
10 |
11 | [ThreadStatic]
12 | private static ByteBuf s_fastBuf;
13 |
14 | public static int MaxPoolCacheNum { get; set; } = 100;
15 |
16 |
17 | private static ByteBufPool Pool => s_pool ?? (s_pool = new ByteBufPool(MaxPoolCacheNum));
18 |
19 | public static TemporalByteBuf GuardAlloc(int? hintSize)
20 | {
21 | return new TemporalByteBuf(Alloc(hintSize));
22 | }
23 |
24 | public static ByteBuf Alloc(int? hintSize)
25 | {
26 | ByteBuf byteBuf = s_fastBuf;
27 | if (byteBuf != null)
28 | {
29 | s_fastBuf = null;
30 | return byteBuf;
31 | }
32 |
33 | return Pool.Alloc(hintSize);
34 | }
35 |
36 | public static void Free(ByteBuf buf)
37 | {
38 | buf.Clear();
39 | if (s_fastBuf == null)
40 | {
41 | s_fastBuf = buf;
42 | }
43 | else
44 | {
45 | Pool.Free(buf);
46 | }
47 | }
48 |
49 | public static byte[] CopyDataThenFree(ByteBuf buf)
50 | {
51 | byte[] result = buf.CopyData();
52 | Free(buf);
53 | return result;
54 | }
55 |
56 | internal static void ResetForTest()
57 | {
58 | s_fastBuf = null;
59 | s_pool = null;
60 | }
61 |
62 | internal static ByteBuf GetFastBufForTest()
63 | {
64 | return s_fastBuf;
65 | }
66 |
67 | internal static Stack GetByteBufsForTest()
68 | {
69 | return Pool.GetByteBufsForTest();
70 | }
71 | }
72 |
73 |
74 | public readonly struct TemporalByteBuf : IDisposable
75 | {
76 | public ByteBuf Buf { get; }
77 |
78 | public TemporalByteBuf(ByteBuf buf)
79 | {
80 | Buf = buf;
81 | }
82 |
83 | public void Dispose()
84 | {
85 | ThreadLocalTemporalByteBufPool.Free(Buf);
86 | }
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/field_type/table_field_type.go:
--------------------------------------------------------------------------------
1 | package field_type
2 |
3 | type TableFieldType struct {
4 | Type EFieldType //类型
5 | Key *TableFieldType //key的类型
6 | Value *TableFieldType //Value的类型
7 | Name string // 枚举或者Class的名字
8 | Class *TableFieldClass // 自定义类
9 | ExtFieldType IExtFieldType // 扩展自定义字段类型
10 | }
11 |
12 | var TableFieldTypeNone = newTableFieldType(EFieldType_None)
13 |
14 | func newTableFieldType(fieldType EFieldType) *TableFieldType {
15 | if fieldType != EFieldType_Enum {
16 | tft, ok := baseFieldType[fieldType]
17 | if ok {
18 | return tft
19 | }
20 | }
21 | tft := &TableFieldType{
22 | Type: fieldType,
23 | Key: nil,
24 | Value: nil,
25 | }
26 | return tft
27 | }
28 |
29 | func newTableFieldEnumType(name string) *TableFieldType {
30 | tft := &TableFieldType{
31 | Type: EFieldType_Enum,
32 | Name: name,
33 | }
34 | return tft
35 | }
36 |
37 | func newTableFieldArrayType(value *TableFieldType) *TableFieldType {
38 | tft := &TableFieldType{
39 | Type: EFieldType_Slice,
40 | Key: nil,
41 | Value: value,
42 | }
43 | return tft
44 | }
45 |
46 | func newTableFieldMapType(key *TableFieldType, value *TableFieldType) *TableFieldType {
47 | tft := &TableFieldType{
48 | Type: EFieldType_Map,
49 | Key: key,
50 | Value: value,
51 | }
52 | return tft
53 | }
54 |
55 | func newTableFieldClassType(class *TableFieldClass) *TableFieldType {
56 | tft := &TableFieldType{
57 | Type: EFieldType_Class,
58 | Class: class,
59 | Name: class.Name,
60 | }
61 | return tft
62 | }
63 |
64 | // SetExtFieldType 设置扩展字段类型
65 | func (tft *TableFieldType) SetExtFieldType(extFieldType IExtFieldType) {
66 | tft.ExtFieldType = extFieldType
67 | }
68 |
69 | // IsBaseType 是否可以作为map的key
70 | func (tft *TableFieldType) IsBaseType() bool {
71 | _, ok := baseFieldType[tft.Type]
72 | return ok
73 | }
74 |
75 | func (tft *TableFieldType) IsComplexType() bool {
76 | switch tft.Type {
77 | case EFieldType_Slice, EFieldType_Map, EFieldType_Class:
78 | return true
79 | default:
80 | return false
81 | }
82 | }
83 |
84 | func (tft *TableFieldType) IsReferenceType() bool {
85 | if tft.IsComplexType() {
86 | return true
87 | }
88 | if tft.Type == EFieldType_String {
89 | return true
90 | }
91 | return false
92 | }
93 |
94 | func (tft *TableFieldType) CreateArrayFieldType() *TableFieldType {
95 | var fieldType = newTableFieldArrayType(tft)
96 | return fieldType
97 | }
98 |
--------------------------------------------------------------------------------
/ext/ext_field/kvlist_int_int.go:
--------------------------------------------------------------------------------
1 | package ext_field
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/fankit/fanstr"
7 | "github.com/821869798/table-export/config"
8 | "github.com/821869798/table-export/field_type"
9 | "strconv"
10 | )
11 |
12 | type ExtFieldKVListIntInt struct {
13 | FieldType *field_type.TableFieldType
14 | }
15 |
16 | func NewExtFieldKVListIntInt() field_type.IExtFieldType {
17 | ft := field_type.NewTableFieldClass("KVList_IntInt")
18 | ft.AddField("keys", field_type.NewTableFieldArrayType(field_type.NewTableFieldType(field_type.EFieldType_Int)))
19 | ft.AddField("values", field_type.NewTableFieldArrayType(field_type.NewTableFieldType(field_type.EFieldType_Int)))
20 |
21 | e := &ExtFieldKVListIntInt{
22 | FieldType: field_type.NewTableFieldClassType(ft),
23 | }
24 |
25 | e.FieldType.SetExtFieldType(e)
26 |
27 | return e
28 | }
29 |
30 | func (e *ExtFieldKVListIntInt) Name() string {
31 | return e.FieldType.Name
32 | }
33 |
34 | func (e *ExtFieldKVListIntInt) DefineFile() string {
35 | return "CfgCommon"
36 | }
37 |
38 | func (e *ExtFieldKVListIntInt) TableFieldType() *field_type.TableFieldType {
39 | return e.FieldType
40 | }
41 |
42 | func (e *ExtFieldKVListIntInt) ParseOriginData(origin string) (interface{}, error) {
43 | strSlice := fanstr.SplitEx(origin, config.GlobalConfig.Table.MapSplit1)
44 | if len(strSlice) == 0 {
45 | return map[string]interface{}{
46 | "keys": []interface{}{},
47 | "values": []interface{}{},
48 | }, nil
49 | }
50 |
51 | keys := make([]interface{}, 0, len(strSlice))
52 | values := make([]interface{}, 0, len(strSlice))
53 | for _, v := range strSlice {
54 | strSlice := fanstr.SplitEx(v, config.GlobalConfig.Table.MapSplit2)
55 | if len(strSlice) == 0 {
56 | continue
57 | }
58 | if len(strSlice) != 2 {
59 | return nil, errors.New(fmt.Sprintf("KVListIntInt ParseOriginData error,need array length 2: [%v]", v))
60 | }
61 | k, err := strconv.Atoi(strSlice[0])
62 | if err != nil {
63 | return nil, errors.New(fmt.Sprintf("KVListIntInt ParseOriginData error,need param left int: [%v]", v))
64 | }
65 | v, err := strconv.Atoi(strSlice[1])
66 | if err != nil {
67 | return nil, errors.New(fmt.Sprintf("KVListIntInt ParseOriginData error,need param right int: [%v]", v))
68 | }
69 | keys = append(keys, int32(k))
70 | values = append(values, int32(v))
71 | }
72 |
73 | return map[string]interface{}{
74 | "keys": keys,
75 | "values": values,
76 | }, nil
77 | }
78 |
--------------------------------------------------------------------------------
/convert/wrap/wrap_class.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/data/env"
9 | "github.com/821869798/table-export/field_type"
10 | )
11 |
12 | type classWrap struct{}
13 |
14 | func (c *classWrap) OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
15 | //TODO implement me
16 | panic("classWrap implement me")
17 | }
18 |
19 | func (c *classWrap) OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
20 | //TODO implement me
21 | panic("classWrap implement me")
22 | }
23 |
24 | func (c *classWrap) OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
25 | switch exportType {
26 | default:
27 | return env.GetMetaRuleUnitPlus().GetClassDefinePrefix() + fieldType.Name, nil
28 | }
29 | }
30 |
31 | func (c *classWrap) FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
32 | if origin == nil {
33 | return nil, nil
34 | }
35 | switch origin.(type) {
36 | case map[string]interface{}:
37 | return origin, nil
38 | case map[string]string:
39 | return origin, nil
40 | default:
41 | return nil, errors.New(fmt.Sprintf("[FormatValueInterface|class] no support type[%T]", origin))
42 | }
43 | }
44 |
45 | func (c *classWrap) DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
46 | //TODO implement me
47 | panic("implement me")
48 | }
49 |
50 | func (c *classWrap) DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
51 | if origin == nil {
52 | visitor.AcceptClassNull(fieldType.Class)
53 | return nil
54 | }
55 | switch value := origin.(type) {
56 | case map[string]interface{}:
57 | visitor.AcceptClass(value, fieldType.Class)
58 | return nil
59 | case map[string]string:
60 | visitor.AcceptClassString(value, fieldType.Class)
61 | return nil
62 | case string:
63 | return RunDataVisitorString(visitor, fieldType, value)
64 | default:
65 | return errors.New(fmt.Sprintf("[DataVisitorValue|class] no support type[%T]", origin))
66 | }
67 | }
68 |
69 | func (c *classWrap) CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
70 | return print.AcceptClass(fieldType, fieldName, reader, depth)
71 | }
72 |
--------------------------------------------------------------------------------
/examples/conf/config.toml:
--------------------------------------------------------------------------------
1 | # 所有的路径填相对exe执行文件的相对路径
2 | # 全局的一些配置
3 | # 处理excel的配置
4 | [table]
5 | # Excel策划配置表目录
6 | src_dir = "./excels"
7 | # 表格字段名行号
8 | name = 0
9 | # 表格描述行号
10 | desc = 4
11 | # 表格数据起始行号,由空号结束
12 | data_start = 5
13 | # 配置数组的分隔符
14 | array_split = "_"
15 | # 配置map的分隔符
16 | map_split1 = "|"
17 | map_split2 = "="
18 |
19 | # 控制导表的元配置
20 | [meta]
21 | # 生成出来的表meta配置临时目录
22 | gen_dir = "./temp_meta"
23 |
24 | [[meta.rules]]
25 | rule_name = "json"
26 | config_dir = "./json_meta"
27 | [meta.rules.json]
28 | json_out = "./output/json"
29 |
30 | [[meta.rules]]
31 | rule_name = "lua"
32 | config_dir = "./lua_meta"
33 | [meta.rules.lua]
34 | lua_out = "./output/lua"
35 | post_process = true #导出配置的后处理开关
36 | temp_dir = "./temp_work/temp_lua"
37 | post_process_lua = "./tools/DataTableOptimizer.lua"
38 | post_work_dir = "./tools"
39 | lua_win_dir = "./tools/lua5.3/lua53.exe"
40 | lua_mac_dir = "./tools/lua5.3/lua53"
41 |
42 | [[meta.rules]]
43 | rule_name = "cs_proto"
44 | config_dir = "./cs_proto_meta"
45 | [meta.rules.cs_proto]
46 | proto_package = "table"
47 | proto_temp_dir = "./temp_work/temp_proto"
48 | proto_cs_dir = "./output/proto_cs"
49 | bytes_dir = "./output/proto_bytes"
50 | protoc_win_dir = "./tools/protoc.exe"
51 | protoc_mac_dir = "./tools/protoc"
52 |
53 | [[meta.rules]]
54 | rule_name = "cs_bin"
55 | config_dir = "./cs_bin_config/table_meta"
56 | [meta.rules.cs_bin]
57 | code_temp_dir = "./temp_work/temp_cs_bin_code"
58 | data_temp_dir = "./temp_work/temp_cs_bin_data"
59 | gen_code_dir = "./projects/proj_cs_bin/src/gen"
60 | data_bin_dir = "./projects/proj_cs_bin/table_bytes"
61 | code_namespace = "CfgTable"
62 | #toml支持多行文本
63 | gen_code_head = """using Serialization;
64 | """
65 | code_not_found_key = """#if UNITY_EDITOR
66 | Debug.LogError($"[%s] config id not found,id:{%s}");
67 | #endif"""
68 | # 以下是生成的优化相关数据
69 | gen_optimize = true
70 | # 枚举文件路径集,可以支持多个,以及*或**匹配
71 | enum_files = [
72 | "./cs_bin_config/enum_define/**/*.toml",
73 | ]
74 | # 枚举,统一添加一个的前缀名,可为空
75 | enum_define_prefix = ""
76 | # 自定义class,统一添加一个的前缀名,可为空
77 | class_define_prefix = ""
78 | # 添加内置代码的扩展类型支持
79 | builtin_field_types = [
80 | "PointInt",
81 | "KVList_IntInt",
82 | "KVList_IntFloat",
83 | ]
84 | # 用脚本实现的扩展类型
85 | script_field_types = [
86 | "./cs_bin_config/scripts/ext_field/**/*.js",
87 | ]
88 | # 全局单次的后处理
89 | post_global_script = "./cs_bin_config/scripts/global/post_global.js"
90 |
91 |
--------------------------------------------------------------------------------
/export/cs_bin/export_code_class.go:
--------------------------------------------------------------------------------
1 | package cs_bin
2 |
3 | import (
4 | "github.com/821869798/fankit/fanstr"
5 | "github.com/821869798/table-export/config"
6 | "github.com/821869798/table-export/convert/printer"
7 | "github.com/821869798/table-export/convert/wrap"
8 | "github.com/821869798/table-export/field_type"
9 | "github.com/gookit/slog"
10 | "os"
11 | "path/filepath"
12 | "text/template"
13 | )
14 |
15 | func GenCSBinCodeExtClass(fileName string, extFieldClasses []*field_type.TableFieldType, csBinRule *config.RawMetaRuleUnitCSBin, outputPath string) {
16 |
17 | // 生成模板数据
18 | templateRoot := &CSCodeWriteExtClassFile{
19 | CodeHead: fanstr.ReplaceWindowsLineEnd(csBinRule.GenCodeHead),
20 | NameSpace: fanstr.ReplaceWindowsLineEnd(csBinRule.GenCodeNamespace),
21 | ClassDefinePrefix: csBinRule.GetClassDefinePrefix(),
22 | }
23 |
24 | for _, extFieldClass := range extFieldClasses {
25 | templateExtClass := &CSCodeWriteExtClass{
26 | Name: extFieldClass.Name,
27 | }
28 | for _, extField := range extFieldClass.Class.AllFields() {
29 | typeDef, err := wrap.GetOutputDefTypeValue(config.ExportType_CS_Bin, extField.Type, false)
30 | if err != nil {
31 | slog.Fatalf("gen cs code error:%v", typeDef)
32 | }
33 | templateExtClass.Fields = append(templateExtClass.Fields, &CSCodeWriteExtClassField{
34 | TypeDef: typeDef,
35 | Name: extField.Name,
36 | FieldType: extField.Type,
37 | })
38 | }
39 | templateRoot.ClassTypes = append(templateRoot.ClassTypes, templateExtClass)
40 | }
41 |
42 | //创建代理打印器
43 | codePrinter := printer.NewCSBinaryPrint(false)
44 |
45 | //目标路径
46 | filePath := filepath.Join(outputPath, fileName+".cs")
47 | file, err := os.Create(filePath)
48 | if err != nil {
49 | slog.Fatal(err)
50 | }
51 |
52 | //创建模板,绑定全局函数,并且解析
53 | tmpl, err := template.New("cs_bin_class").Funcs(template.FuncMap{
54 | "CSbinFieldReader": func(fieldType *field_type.TableFieldType, fieldName string, reader string) string {
55 | return codePrinter.AcceptField(fieldType, fieldName, reader)
56 | },
57 | }).Parse(templateCSCodeClass)
58 |
59 | //渲染输出
60 | err = tmpl.Execute(file, templateRoot)
61 | if err != nil {
62 | slog.Fatal(err)
63 | }
64 |
65 | err = file.Close()
66 | if err != nil {
67 | slog.Fatal(err)
68 | }
69 | }
70 |
71 | type CSCodeWriteExtClassFile struct {
72 | CodeHead string
73 | NameSpace string
74 | ClassDefinePrefix string
75 | ClassTypes []*CSCodeWriteExtClass
76 | }
77 |
78 | type CSCodeWriteExtClass struct {
79 | Name string
80 | Fields []*CSCodeWriteExtClassField
81 | }
82 |
83 | type CSCodeWriteExtClassField struct {
84 | TypeDef string
85 | Name string
86 | FieldType *field_type.TableFieldType
87 | }
88 |
--------------------------------------------------------------------------------
/convert/wrap/wrap_bool.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/field_type"
9 | "strconv"
10 | )
11 |
12 | type boolWrap struct{}
13 |
14 | func (b *boolWrap) OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
15 | if origin == "" {
16 | return false, nil
17 | }
18 | value, err := strconv.ParseBool(origin)
19 | if err != nil {
20 | return nil, err
21 | }
22 | return value, nil
23 | }
24 |
25 | func (b *boolWrap) OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
26 | switch exportType {
27 | default:
28 | if origin == "" {
29 | return strconv.FormatBool(false), nil
30 | }
31 | value, err := strconv.ParseBool(origin)
32 | if err != nil {
33 | return "", err
34 | }
35 | return strconv.FormatBool(value), nil
36 | }
37 | }
38 |
39 | func (b *boolWrap) OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
40 | switch exportType {
41 | case config.ExportType_CS_Bin:
42 | return "bool", nil
43 | default:
44 | return "", errors.New("no support export Type Output DefType")
45 | }
46 | }
47 |
48 | func (b *boolWrap) FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
49 | _, ok := origin.(bool)
50 | if ok {
51 | return origin, nil
52 | }
53 | return nil, errors.New(fmt.Sprintf("[FormatValueInterface|bool] no support type[%T]", origin))
54 | }
55 |
56 | func (b *boolWrap) DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
57 | if origin == "" {
58 | visitor.AcceptBool(false)
59 | return nil
60 | }
61 | value, err := strconv.ParseBool(origin)
62 | if err != nil {
63 | return err
64 | }
65 | visitor.AcceptBool(value)
66 | return nil
67 | }
68 |
69 | func (b *boolWrap) DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
70 | value, ok := origin.(bool)
71 | if ok {
72 | visitor.AcceptBool(value)
73 | return nil
74 | }
75 | stringValue, ok := origin.(string)
76 | if ok {
77 | return b.DataVisitorString(visitor, fieldType, stringValue)
78 | }
79 | return errors.New(fmt.Sprintf("[DataVisitorValue|bool] no support type[%T]", origin))
80 | }
81 |
82 | func (b *boolWrap) CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
83 | return print.AcceptBool(fieldType, fieldName, reader, depth)
84 | }
85 |
--------------------------------------------------------------------------------
/meta/field_parse.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/data/env"
7 | "github.com/821869798/table-export/field_type"
8 | "regexp"
9 | "strings"
10 | )
11 |
12 | var (
13 | regArray = regexp.MustCompile(`^\[\](.+)$`)
14 | regMap = regexp.MustCompile(`^map\[(\w+)\](.+)$`)
15 | regWordGeneric = regexp.MustCompile(`^([\w_]+)<(.+)>$`)
16 | )
17 |
18 | // parseFieldTypeFromString 解析表格字段类型
19 | func parseFieldTypeFromString(origin string) (*field_type.TableFieldType, error) {
20 |
21 | tft := field_type.NewTableFieldType(field_type.EFieldType_None)
22 | switch origin {
23 | case "int":
24 | tft.Type = field_type.EFieldType_Int
25 | case "uint":
26 | tft.Type = field_type.EFieldType_UInt
27 | case "long":
28 | tft.Type = field_type.EFieldType_Long
29 | case "ulong":
30 | tft.Type = field_type.EFieldType_ULong
31 | case "bool":
32 | tft.Type = field_type.EFieldType_Bool
33 | case "float":
34 | tft.Type = field_type.EFieldType_Float
35 | case "double":
36 | tft.Type = field_type.EFieldType_Double
37 | case "string":
38 | tft.Type = field_type.EFieldType_String
39 | default:
40 |
41 | // array
42 | result := regArray.FindAllStringSubmatch(origin, -1)
43 | if len(result) == 1 && len(result[0]) == 2 {
44 | tft.Type = field_type.EFieldType_Slice
45 | subTft, err := parseFieldTypeFromString(strings.TrimSpace(result[0][1]))
46 | if err != nil {
47 | return nil, err
48 | }
49 | tft.Value = subTft
50 | break
51 | }
52 |
53 | // map
54 | result = regMap.FindAllStringSubmatch(origin, -1)
55 | if len(result) == 1 && len(result[0]) == 3 {
56 | tft.Type = field_type.EFieldType_Map
57 | keyTft, err := parseFieldTypeFromString(strings.TrimSpace(result[0][1]))
58 | if err != nil {
59 | return nil, err
60 | }
61 | if !keyTft.IsBaseType() {
62 | return nil, errors.New(fmt.Sprintf("map key type not support[%v]", keyTft))
63 | }
64 | tft.Key = keyTft
65 |
66 | valueTft, err := parseFieldTypeFromString(result[0][2])
67 | if err != nil {
68 | return nil, err
69 | }
70 | tft.Value = valueTft
71 | break
72 | }
73 |
74 | // keyword generic: ext field_type type generic
75 | //result = regWordGeneric.FindAllStringSubmatch(origin, -1)
76 | //if len(result) == 1 && len(result[0]) == 3 {
77 | // // TODO generic support
78 | //}
79 |
80 | // ext field_type type
81 | extFieldType, ok := env.GetExtFieldType(origin)
82 | if ok {
83 | return extFieldType.TableFieldType(), nil
84 | }
85 |
86 | // enum
87 | enumDefine, ok := env.GetEnumDefine(origin)
88 | if ok {
89 | tft.Type = field_type.EFieldType_Enum
90 | tft.Name = enumDefine.Name
91 | break
92 | }
93 |
94 | tft = nil
95 | }
96 |
97 | if tft != nil {
98 | return tft, nil
99 | }
100 | return nil, errors.New("no support type:" + origin)
101 | }
102 |
--------------------------------------------------------------------------------
/convert/wrap/wrap_double.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/field_type"
9 | "strconv"
10 | )
11 |
12 | type doubleWrap struct{}
13 |
14 | func (b *doubleWrap) OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
15 | if origin == "" {
16 | return float64(0), nil
17 | }
18 | value, err := strconv.ParseFloat(origin, 64)
19 | if err != nil {
20 | return nil, err
21 | }
22 | return value, nil
23 | }
24 |
25 | func (b *doubleWrap) OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
26 | switch exportType {
27 | default:
28 | if origin == "" {
29 | return "0", nil
30 | }
31 | _, err := strconv.ParseFloat(origin, 64)
32 | if err != nil {
33 | return "", err
34 | }
35 | return origin, nil
36 | }
37 | }
38 |
39 | func (b *doubleWrap) OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
40 | switch exportType {
41 | case config.ExportType_CS_Bin:
42 | return "double", nil
43 | default:
44 | return "", errors.New("no support export Type Output DefType")
45 | }
46 | }
47 |
48 | func (b *doubleWrap) FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
49 | switch value := origin.(type) {
50 | case float64:
51 | return value, nil
52 | case float32:
53 | return float64(value), nil
54 | case int64:
55 | return float64(value), nil
56 | default:
57 | return nil, errors.New(fmt.Sprintf("[FormatValueInterface|double] no support type[%T]", origin))
58 | }
59 | }
60 |
61 | func (b *doubleWrap) DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
62 | if origin == "" {
63 | visitor.AcceptDouble(0)
64 | return nil
65 | }
66 | value, err := strconv.ParseFloat(origin, 64)
67 | if err != nil {
68 | return err
69 | }
70 | visitor.AcceptDouble(value)
71 | return nil
72 | }
73 |
74 | func (b *doubleWrap) DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
75 | switch value := origin.(type) {
76 | case float64:
77 | visitor.AcceptDouble(value)
78 | return nil
79 | case float32:
80 | visitor.AcceptDouble(float64(value))
81 | return nil
82 | case int64:
83 | visitor.AcceptDouble(float64(value))
84 | return nil
85 | case string:
86 | return b.DataVisitorString(visitor, fieldType, value)
87 | default:
88 | return errors.New(fmt.Sprintf("[DataVisitorValue|double] no support type[%T]", origin))
89 | }
90 | }
91 |
92 | func (b *doubleWrap) CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
93 | return print.AcceptDouble(fieldType, fieldName, reader, depth)
94 | }
95 |
--------------------------------------------------------------------------------
/export/common/load_util.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "github.com/821869798/table-export/config"
5 | "github.com/821869798/table-export/constant"
6 | "github.com/821869798/table-export/data/memory_table"
7 | "github.com/821869798/table-export/data/model"
8 | "github.com/821869798/table-export/data/optimize"
9 | "github.com/821869798/table-export/data/source"
10 | "github.com/821869798/table-export/meta"
11 | "github.com/gookit/slog"
12 | "sync"
13 | )
14 |
15 | // LoadTableModelParallel 通用的加载TableModel的方法
16 | func LoadTableModelParallel(tableMetas []*meta.RawTableMeta, exportFunc func(*model.TableModel)) []*model.TableModel {
17 | wg := sync.WaitGroup{}
18 | wg.Add(len(tableMetas))
19 | allDataModel := make([]*model.TableModel, 0, len(tableMetas))
20 | var mutex sync.Mutex
21 |
22 | for _, tableMeta := range tableMetas {
23 | if tableMeta.Mode == constant.CommentSymbol {
24 | //是注释模式,不导出
25 | wg.Done()
26 | continue
27 | }
28 | go func(tableMeta *meta.RawTableMeta) {
29 | tm, err := meta.NewTableMeta(tableMeta)
30 | if err != nil {
31 | slog.Fatal(err)
32 | }
33 |
34 | dataModel, err := source.GetDataModelByType(tm)
35 | if err != nil {
36 | slog.Fatal(err)
37 | }
38 |
39 | //执行函数
40 | if exportFunc != nil {
41 | exportFunc(dataModel)
42 | }
43 |
44 | mutex.Lock()
45 | allDataModel = append(allDataModel, dataModel)
46 | mutex.Unlock()
47 |
48 | wg.Done()
49 | }(tableMeta)
50 | }
51 | wg.Wait()
52 |
53 | return allDataModel
54 | }
55 |
56 | // LoadTableModelPlusParallel 增强版的并行导出,生成中间层数据,支持配置中预处理,自定义类型等功能
57 | func LoadTableModelPlusParallel(tableMetas []*meta.RawTableMeta, ruleUnit config.MetaRuleUnitPlus, exportFunc func(*model.TableModel)) []*model.TableModel {
58 | wg := sync.WaitGroup{}
59 | wg.Add(len(tableMetas))
60 | allDataModel := make([]*model.TableModel, 0, len(tableMetas))
61 | var mutex sync.Mutex
62 |
63 | for _, tableMeta := range tableMetas {
64 | if tableMeta.Mode == constant.CommentSymbol {
65 | //是注释模式,不导出
66 | wg.Done()
67 | continue
68 | }
69 | go func(tableMeta *meta.RawTableMeta) {
70 | tm, err := meta.NewTableMeta(tableMeta)
71 | if err != nil {
72 | slog.Fatal(err)
73 | }
74 |
75 | dataModel, err := source.GetDataModelByType(tm)
76 | if err != nil {
77 | slog.Fatal(err)
78 | }
79 |
80 | memoryTable, err := memory_table.NewMemTableCommon(dataModel, len(dataModel.RawData), len(dataModel.RawData))
81 |
82 | if err != nil {
83 | slog.Fatal(err)
84 | }
85 | dataModel.MemTable = memoryTable
86 | if ruleUnit.ActiveOptimizeData() {
87 | optimize.OptimizeTableDataRepeat(dataModel)
88 | }
89 |
90 | //执行函数
91 | if exportFunc != nil {
92 | exportFunc(dataModel)
93 | }
94 |
95 | mutex.Lock()
96 | allDataModel = append(allDataModel, dataModel)
97 | mutex.Unlock()
98 |
99 | wg.Done()
100 | }(tableMeta)
101 | }
102 | wg.Wait()
103 |
104 | return allDataModel
105 | }
106 |
--------------------------------------------------------------------------------
/convert/wrap/wrap_float.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/field_type"
9 | "math"
10 | "strconv"
11 | )
12 |
13 | type floatWrap struct{}
14 |
15 | func (b *floatWrap) OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
16 | if origin == "" {
17 | return float32(0), nil
18 | }
19 | value, err := strconv.ParseFloat(origin, 32)
20 | if err != nil {
21 | return nil, err
22 | }
23 | if value > math.MaxFloat32 {
24 | return nil, errors.New("float value can't greater than max float32")
25 | }
26 | return float32(value), nil
27 | }
28 |
29 | func (b *floatWrap) OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
30 | switch exportType {
31 | default:
32 | if origin == "" {
33 | return "0", nil
34 | }
35 | _, err := strconv.ParseFloat(origin, 32)
36 | if err != nil {
37 | return "", err
38 | }
39 | return origin, nil
40 | }
41 | }
42 |
43 | func (b *floatWrap) OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
44 | switch exportType {
45 | case config.ExportType_CS_Bin:
46 | return "float", nil
47 | default:
48 | return "", errors.New("no support export Type Output DefType")
49 | }
50 | }
51 |
52 | func (b *floatWrap) FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
53 | switch value := origin.(type) {
54 | case float32:
55 | return value, nil
56 | case float64:
57 | return float32(value), nil
58 | case int64:
59 | return float32(value), nil
60 | default:
61 | return nil, errors.New(fmt.Sprintf("[FormatValueInterface|float] no support type[%T]", origin))
62 | }
63 | }
64 |
65 | func (b *floatWrap) DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
66 | if origin == "" {
67 | visitor.AcceptFloat(0)
68 | return nil
69 | }
70 | value, err := strconv.ParseFloat(origin, 32)
71 | if err != nil {
72 | return err
73 | }
74 | visitor.AcceptFloat(float32(value))
75 | return nil
76 | }
77 |
78 | func (b *floatWrap) DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
79 | switch value := origin.(type) {
80 | case float32:
81 | visitor.AcceptFloat(value)
82 | return nil
83 | case float64:
84 | visitor.AcceptFloat(float32(value))
85 | return nil
86 | case int64:
87 | visitor.AcceptFloat(float32(value))
88 | return nil
89 | case string:
90 | return b.DataVisitorString(visitor, fieldType, value)
91 | default:
92 | return errors.New(fmt.Sprintf("[DataVisitorValue|float] no support type[%T]", origin))
93 | }
94 | }
95 |
96 | func (b *floatWrap) CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
97 | return print.AcceptFloat(fieldType, fieldName, reader, depth)
98 | }
99 |
--------------------------------------------------------------------------------
/convert/wrap/wrap_int.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/field_type"
9 | "strconv"
10 | )
11 |
12 | type intWrap struct{}
13 |
14 | func (b *intWrap) OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
15 | if origin == "" {
16 | return int32(0), nil
17 | }
18 | value, err := strconv.Atoi(origin)
19 | if err != nil {
20 | return nil, err
21 | }
22 | return int32(value), nil
23 | }
24 |
25 | func (b *intWrap) OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
26 | switch exportType {
27 | default:
28 | if origin == "" {
29 | return "0", nil
30 | }
31 | _, err := strconv.Atoi(origin)
32 | if err != nil {
33 | return "", err
34 | }
35 | return origin, nil
36 | }
37 | }
38 |
39 | func (b *intWrap) OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
40 | switch exportType {
41 | case config.ExportType_CS_Bin:
42 | return "int", nil
43 | default:
44 | return "", errors.New("no support export Type Output DefType")
45 | }
46 | }
47 |
48 | func (b *intWrap) FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
49 | switch value := origin.(type) {
50 | case int32:
51 | return value, nil
52 | case int64:
53 | return int32(value), nil
54 | case int:
55 | return int32(value), nil
56 | case float64:
57 | return int32(value), nil
58 | default:
59 | return nil, errors.New(fmt.Sprintf("[FormatValueInterface|int] no support type[%T]", origin))
60 | }
61 | }
62 |
63 | func (b *intWrap) DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
64 | if origin == "" {
65 | visitor.AcceptInt(0)
66 | return nil
67 | }
68 | value, err := strconv.Atoi(origin)
69 | if err != nil {
70 | return err
71 | }
72 | visitor.AcceptInt(int32(value))
73 | return nil
74 | }
75 |
76 | func (b *intWrap) DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
77 | switch value := origin.(type) {
78 | case int32:
79 | visitor.AcceptInt(value)
80 | return nil
81 | case int64:
82 | visitor.AcceptInt(int32(value))
83 | return nil
84 | case int:
85 | visitor.AcceptInt(int32(value))
86 | return nil
87 | case float64:
88 | visitor.AcceptInt(int32(value))
89 | return nil
90 | case string:
91 | return b.DataVisitorString(visitor, fieldType, value)
92 | default:
93 | return errors.New(fmt.Sprintf("[DataVisitorValue|int] no support type[%T]", origin))
94 | }
95 | }
96 |
97 | func (b *intWrap) CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
98 | return print.AcceptInt(fieldType, fieldName, reader, depth)
99 | }
100 |
--------------------------------------------------------------------------------
/convert/wrap/wrap_long.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/field_type"
9 | "strconv"
10 | )
11 |
12 | type longWrap struct{}
13 |
14 | func (b *longWrap) OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
15 | if origin == "" {
16 | return int64(0), nil
17 | }
18 | value, err := strconv.ParseInt(origin, 10, 64)
19 | if err != nil {
20 | return nil, err
21 | }
22 | return value, nil
23 | }
24 |
25 | func (b *longWrap) OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
26 | switch exportType {
27 | default:
28 | if origin == "" {
29 | return "0", nil
30 | }
31 | _, err := strconv.ParseInt(origin, 10, 64)
32 | if err != nil {
33 | return "", err
34 | }
35 | return origin, nil
36 | }
37 | }
38 |
39 | func (b *longWrap) OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
40 | switch exportType {
41 | case config.ExportType_CS_Bin:
42 | return "long", nil
43 | default:
44 | return "", errors.New("no support export Type Output DefType")
45 | }
46 | }
47 |
48 | func (b *longWrap) FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
49 | switch value := origin.(type) {
50 | case int64:
51 | return value, nil
52 | case int32:
53 | return int64(value), nil
54 | case int:
55 | return int64(value), nil
56 | case float64:
57 | return int64(value), nil
58 | default:
59 | return nil, errors.New(fmt.Sprintf("[FormatValueInterface|long] no support type[%T]", origin))
60 | }
61 | }
62 |
63 | func (b *longWrap) DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
64 | if origin == "" {
65 | visitor.AcceptLong(0)
66 | return nil
67 | }
68 | value, err := strconv.ParseInt(origin, 10, 64)
69 | if err != nil {
70 | return err
71 | }
72 | visitor.AcceptLong(value)
73 | return nil
74 | }
75 |
76 | func (b *longWrap) DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
77 | switch value := origin.(type) {
78 | case int64:
79 | visitor.AcceptLong(value)
80 | return nil
81 | case int32:
82 | visitor.AcceptLong(int64(value))
83 | return nil
84 | case int:
85 | visitor.AcceptLong(int64(value))
86 | return nil
87 | case float64:
88 | visitor.AcceptLong(int64(value))
89 | return nil
90 | case string:
91 | return b.DataVisitorString(visitor, fieldType, value)
92 | default:
93 | return errors.New(fmt.Sprintf("[DataVisitorValue|long] no support type[%T]", origin))
94 | }
95 | }
96 |
97 | func (b *longWrap) CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
98 | return print.AcceptLong(fieldType, fieldName, reader, depth)
99 | }
100 |
--------------------------------------------------------------------------------
/data/optimize/optimize_repeat.go:
--------------------------------------------------------------------------------
1 | package optimize
2 |
3 | import (
4 | "github.com/821869798/table-export/data/model"
5 | "github.com/821869798/table-export/meta"
6 | )
7 |
8 | // OptimizeTableDataRepeat 优化重复的数据
9 | func OptimizeTableDataRepeat(dataModel *model.TableModel) {
10 | //除了Key以外没有其他字段,不需要优化
11 | if dataModel.Meta.NotKeyFieldCount() <= 0 {
12 | return
13 | }
14 | refTableFields := make([]*OptimizeProcess, 0, dataModel.Meta.NotKeyFieldCount())
15 | for _, f := range dataModel.Meta.Fields {
16 | if f.Key > 0 {
17 | //key不需要生成
18 | continue
19 | }
20 | if f.Type.IsReferenceType() {
21 | refTableFields = append(refTableFields, newOptimizeProcess(f))
22 | }
23 | }
24 |
25 | // 检测每个字段的重复是否超过阈值
26 | for _, rowData := range dataModel.RawData {
27 | for _, refField := range refTableFields {
28 | rawIndex := dataModel.NameIndexMapping[refField.Field.Target]
29 | var rawStr string
30 | if rawIndex < len(rowData) {
31 | rawStr = rowData[rawIndex]
32 | }
33 | count, ok := refField.CellCounts[rawStr]
34 | if ok {
35 | refField.CellCounts[rawStr] = count + 1
36 | refField.RepeatedCount++
37 | } else {
38 | refField.CellCounts[rawStr] = 1
39 | }
40 | }
41 | }
42 |
43 | result := genOptimizeRefTableData(dataModel, refTableFields)
44 | if result != nil {
45 | dataModel.Optimize = result
46 | }
47 | }
48 |
49 | func genOptimizeRefTableData(dataModel *model.TableModel, refTableFields []*OptimizeProcess) *model.TableOptimize {
50 | tableOptimize := model.NewTableOptimize()
51 | optimizeFields := make([]*OptimizeProcess, 0)
52 | for _, refField := range refTableFields {
53 | refField.TableOptimizeField = model.NewTableOptimizeField(refField.Field, len(refField.CellCounts), len(dataModel.RawData))
54 | refField.CellCounts = make(map[string]int)
55 | refField.DataIndex = 0
56 | optimizeFields = append(optimizeFields, refField)
57 |
58 | tableOptimize.AddOptimizeField(refField.TableOptimizeField)
59 | }
60 | if len(optimizeFields) < 0 {
61 | return nil
62 | }
63 | for rowIndex, rowData := range dataModel.RawData {
64 | for _, refField := range optimizeFields {
65 | rawIndex := dataModel.NameIndexMapping[refField.Field.Target]
66 | var rawStr string
67 | if rawIndex < len(rowData) {
68 | rawStr = rowData[rawIndex]
69 | }
70 | lastIndex, ok := refField.CellCounts[rawStr]
71 | if ok {
72 | refField.TableOptimizeField.DataUseIndex[rowIndex] = lastIndex
73 | } else {
74 | refField.TableOptimizeField.OptimizeDataInTableRow[refField.DataIndex] = rowIndex
75 | refField.TableOptimizeField.DataUseIndex[rowIndex] = refField.DataIndex
76 | refField.CellCounts[rawStr] = refField.DataIndex
77 | refField.DataIndex++
78 | }
79 | }
80 | }
81 | return tableOptimize
82 | }
83 |
84 | type OptimizeProcess struct {
85 | Field *meta.TableField
86 | CellCounts map[string]int
87 | RepeatedCount int
88 | DataIndex int
89 | TableOptimizeField *model.TableOptimizeField
90 | }
91 |
92 | func newOptimizeProcess(f *meta.TableField) *OptimizeProcess {
93 | op := &OptimizeProcess{
94 | Field: f,
95 | CellCounts: make(map[string]int, 0),
96 | RepeatedCount: 0,
97 | }
98 | return op
99 | }
100 |
--------------------------------------------------------------------------------
/convert/wrap/wrap_ulong.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/field_type"
9 | "strconv"
10 | )
11 |
12 | type ulongWrap struct{}
13 |
14 | func (b *ulongWrap) OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
15 | if origin == "" {
16 | return uint64(0), nil
17 | }
18 | value, err := strconv.ParseUint(origin, 10, 64)
19 | if err != nil {
20 | return nil, err
21 | }
22 | return value, nil
23 | }
24 |
25 | func (b *ulongWrap) OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
26 | switch exportType {
27 | default:
28 | if origin == "" {
29 | return "0", nil
30 | }
31 | _, err := strconv.ParseUint(origin, 10, 64)
32 | if err != nil {
33 | return "", err
34 | }
35 | return origin, nil
36 | }
37 | }
38 |
39 | func (b *ulongWrap) OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
40 | switch exportType {
41 | case config.ExportType_CS_Bin:
42 | return "ulong", nil
43 | default:
44 | return "", errors.New("no support export Type Output DefType")
45 | }
46 | }
47 |
48 | func (b *ulongWrap) FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
49 | switch value := origin.(type) {
50 | case uint64:
51 | return value, nil
52 | case uint32:
53 | return uint64(value), nil
54 | case int:
55 | return uint64(value), nil
56 | case float64:
57 | return uint64(value), nil
58 | case int64:
59 | return uint64(value), nil
60 | case int32:
61 | return uint64(value), nil
62 | default:
63 | return nil, errors.New(fmt.Sprintf("[FormatValueInterface|ulong] no support type[%T]", origin))
64 | }
65 | }
66 |
67 | func (b *ulongWrap) DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
68 | if origin == "" {
69 | visitor.AcceptULong(0)
70 | return nil
71 | }
72 | value, err := strconv.ParseUint(origin, 10, 64)
73 | if err != nil {
74 | return err
75 | }
76 | visitor.AcceptULong(value)
77 | return nil
78 | }
79 |
80 | func (b *ulongWrap) DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
81 | switch value := origin.(type) {
82 | case uint64:
83 | visitor.AcceptULong(value)
84 | return nil
85 | case uint32:
86 | visitor.AcceptULong(uint64(value))
87 | return nil
88 | case int:
89 | visitor.AcceptULong(uint64(value))
90 | return nil
91 | case float64:
92 | visitor.AcceptULong(uint64(value))
93 | return nil
94 | case int64:
95 | visitor.AcceptULong(uint64(value))
96 | return nil
97 | case int32:
98 | visitor.AcceptULong(uint64(value))
99 | return nil
100 | case string:
101 | return b.DataVisitorString(visitor, fieldType, value)
102 | default:
103 | return errors.New(fmt.Sprintf("[DataVisitorValue|ulong] no support type[%T]", origin))
104 | }
105 | }
106 |
107 | func (b *ulongWrap) CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
108 | return print.AcceptULong(fieldType, fieldName, reader, depth)
109 | }
110 |
--------------------------------------------------------------------------------
/meta/raw_meta.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | import (
4 | "github.com/821869798/fankit/fanpath"
5 | "github.com/BurntSushi/toml"
6 | "github.com/gookit/slog"
7 | "os"
8 | "path/filepath"
9 | "text/template"
10 | )
11 |
12 | type RawTableMeta struct {
13 | Target string
14 | Mode string
15 | SourceType string `toml:"source_type"`
16 | PostScript string `toml:"post_script"`
17 | Sources []*RawTableSource `toml:"sources"`
18 | Fields []*RawTableField `toml:"fields"`
19 | Checks []*RawTableCheck `toml:"checks"`
20 | }
21 |
22 | type RawTableSource struct {
23 | Table string `toml:"file_name"`
24 | Sheet string `toml:"sheet_name"`
25 | }
26 |
27 | type RawTableField struct {
28 | Active bool `toml:"active"`
29 | Source string `toml:"sname"`
30 | Target string `toml:"tname"`
31 | Type string `toml:"type"`
32 | Key int `toml:"key"`
33 | Desc string `toml:"desc"`
34 | }
35 |
36 | type RawTableCheck struct {
37 | Active bool `tomm:"active"`
38 | Global bool `toml:"global"`
39 | Code string `toml:"code"`
40 | }
41 |
42 | func NewRawTableMeta() *RawTableMeta {
43 | r := &RawTableMeta{}
44 | return r
45 | }
46 |
47 | func NewRawTableField(source, desc string) *RawTableField {
48 | rtf := &RawTableField{
49 | Active: false,
50 | Source: source,
51 | Target: source,
52 | Type: "",
53 | Key: 0,
54 | Desc: desc,
55 | }
56 | return rtf
57 | }
58 |
59 | func LoadTableMetasByDir(fullPath string) ([]*RawTableMeta, error) {
60 | fileLists, err := fanpath.GetFileListByExt(fullPath, ".toml")
61 | if err != nil {
62 | return nil, err
63 | }
64 | tableMetas := make([]*RawTableMeta, len(fileLists), len(fileLists))
65 | for index, file := range fileLists {
66 | slog.Debug(file)
67 | if _, err = toml.DecodeFile(file, &tableMetas[index]); err != nil {
68 | return nil, err
69 | }
70 |
71 | }
72 | return tableMetas, nil
73 | }
74 |
75 | func (rtm *RawTableMeta) SaveTableMetaByDir(filePath string) error {
76 | parentDir := filepath.Dir(filePath)
77 | //不存在就创建
78 | if !fanpath.ExistDir(parentDir) {
79 | _ = os.MkdirAll(parentDir, os.ModePerm)
80 | }
81 | file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0755)
82 | if err != nil {
83 | return err
84 | }
85 | defer file.Close()
86 | err = toml.NewEncoder(file).Encode(rtm)
87 | return err
88 | }
89 |
90 | func (rtm *RawTableMeta) SaveTableMetaTemplateByDir(filePath string) error {
91 | tmpl, err := template.New("lua").Parse(`target = "{{.Target}}"
92 | mode = "{{.Mode}}"
93 | source_type = "{{.SourceType}}"
94 | post_script = ""
95 |
96 | sources = [
97 | {{range $i, $v := .Sources }} { file_name = "{{$v.Table}}", sheet_name = "{{$v.Sheet}}" },
98 | {{end}}]
99 |
100 | fields = [
101 | {{range $i, $v := .Fields }} { active = {{$v.Active}}, sname = "{{$v.Source}}" , tname = "{{$v.Target}}" , type = "{{$v.Type}}" , key = {{$v.Key}}, desc = "{{$v.Desc}}" },
102 | {{end}}]
103 |
104 | checks = [
105 | ]
106 | `)
107 | if err != nil {
108 | return err
109 | }
110 |
111 | parentDir := filepath.Dir(filePath)
112 | //不存在就创建
113 | if !fanpath.ExistDir(parentDir) {
114 | _ = os.MkdirAll(parentDir, os.ModePerm)
115 | }
116 | file, err := os.Create(filePath)
117 | if err != nil {
118 | return err
119 | }
120 | defer file.Close()
121 | //渲染输出
122 | err = tmpl.Execute(file, rtm)
123 | if err != nil {
124 | return err
125 | }
126 | return nil
127 | }
128 |
--------------------------------------------------------------------------------
/convert/wrap/wrap_uint.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/field_type"
9 | "math"
10 | "strconv"
11 | )
12 |
13 | type uintWrap struct{}
14 |
15 | func (b *uintWrap) OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
16 | if origin == "" {
17 | return uint32(0), nil
18 | }
19 | value, err := strconv.ParseUint(origin, 10, 32)
20 | if err != nil {
21 | return nil, err
22 | }
23 | if value > math.MaxUint32 {
24 | return nil, errors.New("uint value can't greater than max uint32")
25 | }
26 | return uint32(value), nil
27 | }
28 |
29 | func (b *uintWrap) OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
30 | switch exportType {
31 | default:
32 | if origin == "" {
33 | return "0", nil
34 | }
35 | _, err := strconv.ParseUint(origin, 10, 32)
36 | if err != nil {
37 | return "", err
38 | }
39 | return origin, nil
40 | }
41 | }
42 |
43 | func (b *uintWrap) OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
44 | switch exportType {
45 | case config.ExportType_CS_Bin:
46 | return "uint", nil
47 | default:
48 | return "", errors.New("no support export Type Output DefType")
49 | }
50 | }
51 |
52 | func (b *uintWrap) FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
53 | switch value := origin.(type) {
54 | case uint32:
55 | return value, nil
56 | case uint64:
57 | return uint32(value), nil
58 | case int:
59 | return uint32(value), nil
60 | case float64:
61 | return uint32(value), nil
62 | case int64:
63 | return uint32(value), nil
64 | case int32:
65 | return uint32(value), nil
66 | default:
67 | return nil, errors.New(fmt.Sprintf("[FormatValueInterface|uint] no support type[%T]", origin))
68 | }
69 | }
70 |
71 | func (b *uintWrap) DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
72 | if origin == "" {
73 | visitor.AcceptUInt(0)
74 | return nil
75 | }
76 | value, err := strconv.ParseUint(origin, 10, 32)
77 | if err != nil {
78 | return err
79 | }
80 | visitor.AcceptUInt(uint32(value))
81 | return nil
82 | }
83 |
84 | func (b *uintWrap) DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
85 | switch value := origin.(type) {
86 | case uint32:
87 | visitor.AcceptUInt(value)
88 | return nil
89 | case uint64:
90 | visitor.AcceptUInt(uint32(value))
91 | return nil
92 | case int:
93 | visitor.AcceptUInt(uint32(value))
94 | return nil
95 | case float64:
96 | visitor.AcceptUInt(uint32(value))
97 | return nil
98 | case int64:
99 | visitor.AcceptUInt(uint32(value))
100 | return nil
101 | case int32:
102 | visitor.AcceptUInt(uint32(value))
103 | return nil
104 | case string:
105 | return b.DataVisitorString(visitor, fieldType, value)
106 | default:
107 | return errors.New(fmt.Sprintf("[DataVisitorValue|uint] no support type[%T]", origin))
108 | }
109 | }
110 |
111 | func (b *uintWrap) CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
112 | return print.AcceptUInt(fieldType, fieldName, reader, depth)
113 | }
114 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/src/gen/Tblcsv_test.cs:
--------------------------------------------------------------------------------
1 | using Serialization;
2 |
3 | using System.Collections.Generic;
4 |
5 | namespace CfgTable
6 | {
7 |
8 | public partial class Tblcsv_test
9 | {
10 |
11 | private readonly Dictionary _dataMap;
12 | private readonly List _dataList;
13 |
14 |
15 | public Tblcsv_test(ByteBuf _buf)
16 | {
17 | //first read common data
18 | _TbCommoncsv_test _commonData = null;
19 | var commonSize = _buf.ReadSize();
20 | if( commonSize > 0)
21 | {
22 | _commonData = new _TbCommoncsv_test(_buf);
23 | }
24 |
25 | var size = _buf.ReadSize();
26 | _dataMap = new Dictionary(16);
27 | _dataList = new List(size);
28 |
29 | for (int i = 0; i < size; i++)
30 | {
31 | Cfgcsv_test _v;
32 | _v = Cfgcsv_test.DeserializeCfgcsv_test(_buf, _commonData);
33 | _dataList.Add(_v);
34 | _dataMap[_v.id] = _v;
35 | }
36 |
37 |
38 | PostInit();
39 | }
40 |
41 | public int DataCount => _dataList.Count;
42 | public Dictionary DataMap => _dataMap;
43 | public List DataList => _dataList;
44 |
45 | public Cfgcsv_test Get(int __k0)
46 | {
47 | if (_dataMap.TryGetValue(__k0, out var __tmpv0)) { return __tmpv0; }
48 | #if UNITY_EDITOR
49 | Debug.LogError($"[Tblcsv_test] config id not found,id:{__k0.ToString()}");
50 | #endif
51 | return null;
52 | }
53 |
54 | public Cfgcsv_test GetWithoutError(int __k0)
55 | {
56 | if (_dataMap.TryGetValue(__k0, out var __tmpv0)) { return __tmpv0; }
57 | return null;
58 | }
59 |
60 |
61 | ///
62 | /// table data file
63 | ///
64 | public static string TableFileName { get; } = "csv_test";
65 |
66 | ///
67 | /// post process table
68 | ///
69 | partial void PostInit();
70 |
71 | }
72 |
73 | public partial class Cfgcsv_test
74 | {
75 | private Cfgcsv_test(ByteBuf _buf, _TbCommoncsv_test _commonData)
76 | {
77 |
78 | id = _buf.ReadInt();
79 | { int dataIndex = _buf.ReadInt() - 1; name = _commonData._field0[dataIndex]; }
80 | age = _buf.ReadInt();
81 | PostInit();
82 | }
83 |
84 | internal static Cfgcsv_test DeserializeCfgcsv_test(ByteBuf _buf, _TbCommoncsv_test _commonData)
85 | {
86 | return new Cfgcsv_test(_buf, _commonData);
87 | }
88 |
89 | ///
90 | /// id
91 | ///
92 | public int id { get; private set; }
93 |
94 | ///
95 | /// 名字
96 | ///
97 | public string name { get; private set; }
98 |
99 | ///
100 | /// 年龄
101 | ///
102 | public int age { get; private set; }
103 |
104 |
105 | ///
106 | /// post process table
107 | ///
108 | partial void PostInit();
109 | }
110 |
111 | ///
112 | /// internal common data optimize
113 | ///
114 | internal class _TbCommoncsv_test
115 | {
116 |
117 | internal string[] _field0 { get; private set; }
118 | internal _TbCommoncsv_test(ByteBuf _buf)
119 | {
120 |
121 | {int __n0 = _buf.ReadSize(); _field0 = new string[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ string __v0; __v0 = _buf.ReadString(); _field0[__i0] = __v0; } }
122 | }
123 |
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/meta/gen_meta.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | import (
4 | "encoding/csv"
5 | "github.com/821869798/fankit/fanpath"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/constant"
8 | "github.com/gookit/slog"
9 | "github.com/xuri/excelize/v2"
10 | "os"
11 | "strconv"
12 | "strings"
13 | )
14 |
15 | type GenMeta struct {
16 | genSource string
17 | }
18 |
19 | func NewGenMeta(genSource string) *GenMeta {
20 | g := &GenMeta{
21 | genSource: genSource,
22 | }
23 | return g
24 | }
25 |
26 | func (g *GenMeta) Run() {
27 |
28 | sourceSlice := strings.Split(g.genSource, ",")
29 |
30 | inputOk := false
31 | isCsv := false
32 | if len(sourceSlice) == 2 && strings.HasSuffix(sourceSlice[1], ".csv") {
33 | inputOk = true
34 | isCsv = true
35 | }
36 | if len(sourceSlice) == 3 && !strings.HasSuffix(sourceSlice[1], ".csv") {
37 | inputOk = true
38 | }
39 |
40 | if !inputOk {
41 | slog.Fatalf("generator source arg error! GenSource:%s", g.genSource)
42 | }
43 |
44 | targetName := sourceSlice[0]
45 | srcFileName := sourceSlice[1]
46 |
47 | filePath := fanpath.RelExecuteDir(config.GlobalConfig.Table.SrcDir, srcFileName)
48 |
49 | if !fanpath.ExistFile(filePath) {
50 | slog.Fatalf("generator source source file path not exist! FilePath:%s", filePath)
51 | }
52 |
53 | sheetName := ""
54 | var rows [][]string
55 | var err error
56 | if !isCsv {
57 | sheetName = sourceSlice[2]
58 | rows, err = readExcelFile(filePath, sheetName)
59 | } else {
60 | rows, err = readCsvFile(filePath)
61 | }
62 |
63 | if err != nil {
64 | slog.Fatal(err)
65 | }
66 |
67 | if len(rows) < config.GlobalConfig.Table.DataStart {
68 | slog.Fatal("excel source row count must >= " + strconv.Itoa(config.GlobalConfig.Table.DataStart))
69 | }
70 |
71 | rtm := NewRawTableMeta()
72 | rtm.Target = targetName
73 | rtm.Mode = ""
74 | if isCsv {
75 | rtm.SourceType = "csv"
76 | } else {
77 | rtm.SourceType = "excel"
78 | }
79 |
80 | rtm.Sources = []*RawTableSource{
81 | &RawTableSource{
82 | Table: srcFileName,
83 | Sheet: sheetName,
84 | },
85 | }
86 | rtm.Fields = make([]*RawTableField, 0)
87 | fieldSet := make(map[string]bool)
88 | nameCols := rows[config.GlobalConfig.Table.Name]
89 | descCols := rows[config.GlobalConfig.Table.Desc]
90 | for index, cellStr := range nameCols {
91 | if cellStr == "" {
92 | continue
93 | }
94 | //判断是否重复
95 | _, ok := fieldSet[cellStr]
96 | if ok {
97 | slog.Fatalf("excel source field name[%s] repeated!", cellStr)
98 | }
99 | fieldSet[cellStr] = true
100 | descCellStr := ""
101 | if index < len(descCols) {
102 | descCellStr = descCols[index]
103 | }
104 | rtf := NewRawTableField(cellStr, descCellStr)
105 | rtm.Fields = append(rtm.Fields, rtf)
106 | }
107 |
108 | genFilePath := fanpath.RelExecuteDir(config.GlobalConfig.Meta.GenDir, targetName+constant.MetaFileSuffix)
109 | err = rtm.SaveTableMetaTemplateByDir(genFilePath)
110 | if err != nil {
111 | slog.Fatal(err)
112 | }
113 | }
114 |
115 | func readExcelFile(filePath string, sheetName string) ([][]string, error) {
116 | f, err := excelize.OpenFile(filePath)
117 | if err != nil {
118 | return nil, err
119 | }
120 |
121 | return f.GetRows(sheetName)
122 | }
123 |
124 | func readCsvFile(filePath string) ([][]string, error) {
125 | csvFile, err := os.Open(filePath)
126 | if err != nil {
127 | return nil, err
128 | }
129 | csvReader := csv.NewReader(csvFile) //创建一个新的写入文件流
130 | csvReader.LazyQuotes = true
131 | rowData, err := csvReader.ReadAll()
132 | if err != nil {
133 | return nil, err
134 | }
135 | //去除utf-8的bom
136 | if len(rowData) > 0 && len(rowData[0]) > 0 {
137 | firstData := rowData[0][0]
138 | rowData[0][0] = strings.TrimPrefix(firstData, "\uFEFF")
139 | }
140 | return rowData, nil
141 | }
142 |
--------------------------------------------------------------------------------
/convert/wrap/wrap_enum.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/data/env"
9 | "github.com/821869798/table-export/field_type"
10 | "strconv"
11 | "strings"
12 | )
13 |
14 | type enumWrap struct{}
15 |
16 | func convertEnumToInt(fieldType *field_type.TableFieldType, origin string) (int32, error) {
17 | enumDefine, ok := env.GetEnumDefine(fieldType.Name)
18 | if !ok {
19 | return 0, errors.New(fmt.Sprintf("no support enum type[%v]", fieldType.Name))
20 | }
21 | if enumDefine.Parse4String {
22 | enumValue, ok := enumDefine.ValueStrMapping[strings.TrimSpace(origin)]
23 | if !ok {
24 | return 0, errors.New(fmt.Sprintf("no support enum type[%v] value[%v]", fieldType.Name, origin))
25 | }
26 | return enumValue.Index, nil
27 | }
28 | if origin == "" {
29 | return int32(0), nil
30 | }
31 | value, err := strconv.Atoi(origin)
32 | if err != nil {
33 | return 0, err
34 | }
35 | result := int32(value)
36 | _, ok = enumDefine.ValueMapping[result]
37 | if !ok {
38 | return 0, errors.New(fmt.Sprintf("no support enum type[%v] value[%v]", fieldType.Name, origin))
39 | }
40 | return result, nil
41 | }
42 |
43 | func (e *enumWrap) OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
44 | return convertEnumToInt(fieldType, origin)
45 | }
46 |
47 | func (e *enumWrap) OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
48 | switch exportType {
49 | default:
50 | value, err := convertEnumToInt(fieldType, origin)
51 | if err != nil {
52 | return "", err
53 | }
54 | return strconv.Itoa(int(value)), nil
55 | }
56 | }
57 |
58 | func (e *enumWrap) OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
59 | switch exportType {
60 | case config.ExportType_CS_Bin:
61 | return env.GetMetaRuleUnitPlus().GetEnumDefinePrefix() + fieldType.Name, nil
62 | default:
63 | return "", errors.New("no support export Type Output DefType")
64 | }
65 | }
66 |
67 | func (e *enumWrap) FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
68 | switch value := origin.(type) {
69 | case int32:
70 | return value, nil
71 | case int64:
72 | return int32(value), nil
73 | case int:
74 | return int32(value), nil
75 | case float64:
76 | return int32(value), nil
77 | default:
78 | return nil, errors.New(fmt.Sprintf("[FormatValueInterface|enum] no support type[%T]", origin))
79 | }
80 | }
81 |
82 | func (e *enumWrap) DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
83 | value, err := convertEnumToInt(fieldType, origin)
84 | if err != nil {
85 | return err
86 | }
87 | visitor.AcceptInt(value)
88 | return nil
89 | }
90 |
91 | func (e *enumWrap) DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
92 | switch value := origin.(type) {
93 | case int32:
94 | visitor.AcceptInt(value)
95 | return nil
96 | case int64:
97 | visitor.AcceptInt(int32(value))
98 | return nil
99 | case int:
100 | visitor.AcceptInt(int32(value))
101 | return nil
102 | case float64:
103 | visitor.AcceptInt(int32(value))
104 | return nil
105 | case string:
106 | return e.DataVisitorString(visitor, fieldType, value)
107 | default:
108 | return errors.New(fmt.Sprintf("[DataVisitorValue|enum] no support type[%T]", origin))
109 | }
110 | }
111 |
112 | func (e *enumWrap) CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
113 | return print.AcceptEnum(fieldType, fieldName, reader, depth)
114 | }
115 |
--------------------------------------------------------------------------------
/convert/wrap/wrap_slice.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/field_type"
9 | "strings"
10 | )
11 |
12 | type sliceWrap struct{}
13 |
14 | func (b *sliceWrap) OutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
15 | origin = strings.TrimSpace(origin)
16 | strSlice := strings.Split(origin, config.GlobalConfig.Table.ArraySplit)
17 | result := make([]interface{}, 0, len(strSlice))
18 | if origin != "" {
19 | for _, v := range strSlice {
20 | content, err := GetOutputValue(exportType, fieldType.Value, v)
21 | if err != nil {
22 | return nil, err
23 | }
24 | result = append(result, content)
25 | }
26 | }
27 | return result, nil
28 | }
29 |
30 | func (b *sliceWrap) OutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
31 | switch exportType {
32 | case config.ExportType_Lua:
33 | origin = strings.TrimSpace(origin)
34 | strSlice := strings.Split(origin, config.GlobalConfig.Table.ArraySplit)
35 | result := "{"
36 | if origin != "" {
37 | for _, v := range strSlice {
38 | content, err := GetOutputStringValue(exportType, fieldType.Value, v)
39 | if err != nil {
40 | return "", err
41 | }
42 | result += content + ","
43 | }
44 | }
45 | result += "}"
46 | return result, nil
47 | default:
48 | return "", errors.New(fmt.Sprintf("OutputStringValue slice no support export type[%v]", exportType))
49 | }
50 | }
51 |
52 | func (b *sliceWrap) OutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
53 | switch exportType {
54 | case config.ExportType_CS_Bin:
55 | valueDef, err := GetOutputDefTypeValue(exportType, fieldType.Value, collectionReadonly)
56 | if err != nil {
57 | return "", err
58 | }
59 | var result string
60 | if collectionReadonly {
61 | result = fmt.Sprintf("IReadOnlyList<%s>", valueDef)
62 | } else {
63 | result = fmt.Sprintf("%s[]", valueDef)
64 | }
65 |
66 | return result, nil
67 | default:
68 | return "", errors.New("no support export Type Output DefType")
69 | }
70 | }
71 |
72 | func (b *sliceWrap) FormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
73 | switch origin.(type) {
74 | case []interface{}:
75 | return origin, nil
76 | case []string:
77 | return origin, nil
78 | default:
79 | return nil, errors.New(fmt.Sprintf("[FormatValueInterface|slice] no support type[%T]", origin))
80 | }
81 | }
82 |
83 | func (b *sliceWrap) DataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
84 | origin = strings.TrimSpace(origin)
85 | if origin == "" {
86 | visitor.AcceptStringArray(EmptyStringArray, fieldType.Value)
87 | return nil
88 | }
89 | strSlice := strings.Split(origin, config.GlobalConfig.Table.ArraySplit)
90 | visitor.AcceptStringArray(strSlice, fieldType.Value)
91 | return nil
92 | }
93 |
94 | func (b *sliceWrap) DataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
95 | switch value := origin.(type) {
96 | case []interface{}:
97 | visitor.AcceptArray(value, fieldType.Value)
98 | return nil
99 | case []string:
100 | visitor.AcceptStringArray(value, fieldType.Value)
101 | return nil
102 | case string:
103 | return b.DataVisitorString(visitor, fieldType, value)
104 | default:
105 | return errors.New(fmt.Sprintf("[DataVisitorValue|slice] no support type[%T]", origin))
106 | }
107 | }
108 |
109 | func (b *sliceWrap) CodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
110 | return print.AcceptArray(fieldType, fieldName, reader, depth)
111 | }
112 |
--------------------------------------------------------------------------------
/export/cs_bin/export_cs_bin.go:
--------------------------------------------------------------------------------
1 | package cs_bin
2 |
3 | import (
4 | "github.com/821869798/fankit/fanpath"
5 | "github.com/821869798/table-export/config"
6 | "github.com/821869798/table-export/convert/visitor"
7 | "github.com/821869798/table-export/data/enum"
8 | "github.com/821869798/table-export/data/env"
9 | "github.com/821869798/table-export/data/model"
10 | "github.com/821869798/table-export/export/api"
11 | "github.com/821869798/table-export/export/common"
12 | "github.com/821869798/table-export/field_type"
13 | "github.com/821869798/table-export/meta"
14 | "github.com/821869798/table-export/serialization"
15 | "github.com/821869798/table-export/util"
16 | "github.com/gookit/slog"
17 | "os"
18 | "sync"
19 | "time"
20 | )
21 |
22 | type ExportCSBin struct {
23 | tableMetas []*meta.RawTableMeta
24 | extraArg map[string]string
25 | }
26 |
27 | func NewExportCSBin(tableMetas []*meta.RawTableMeta, extraArg map[string]string) api.IExport {
28 | e := &ExportCSBin{
29 | tableMetas: tableMetas,
30 | extraArg: extraArg,
31 | }
32 | return e
33 | }
34 |
35 | func (e *ExportCSBin) TableMetas() []*meta.RawTableMeta {
36 | return e.tableMetas
37 | }
38 |
39 | func (e *ExportCSBin) Export(ru config.MetaRuleUnit) {
40 |
41 | csBinRule, ok := ru.(*config.RawMetaRuleUnitCSBin)
42 | if !ok {
43 | slog.Fatal("Export CSBin expect *RawMetaRuleUnitJson Rule Unit")
44 | }
45 |
46 | //清空目录
47 | if err := fanpath.InitDirAndClearFile(fanpath.RelExecuteDir(csBinRule.DataTempDir), `^.*?\.bytes$`); err != nil {
48 | slog.Fatal(err)
49 | }
50 |
51 | if err := fanpath.InitDirAndClearFile(fanpath.RelExecuteDir(csBinRule.CodeTempDir), `^.*?\.cs`); err != nil {
52 | slog.Fatal(err)
53 | }
54 |
55 | defer util.TimeCost(time.Now(), "export c# bin time cost = %v\n")
56 |
57 | // 生成导出数据
58 | allDataModel := common.ExportPlusCommon(e.tableMetas, csBinRule)
59 |
60 | //生成代码和二进制数据
61 | wg := sync.WaitGroup{}
62 | wg.Add(len(allDataModel))
63 | for _, tableModel := range allDataModel {
64 | go func(tm *model.TableModel) {
65 | GenCSBinCodeTable(tm, csBinRule, csBinRule.CodeTempDir)
66 | GenCSBinData(tm, csBinRule.DataTempDir)
67 | wg.Done()
68 | }(tableModel)
69 | }
70 | // 生成枚举代码文件
71 | enumFiles := env.EnumFiles()
72 | wg.Add(len(enumFiles))
73 | for _, enumFile := range enumFiles {
74 | go func(ef *enum.DefineEnumFile) {
75 | GenCSBinCodeEnum(ef, csBinRule, csBinRule.CodeTempDir)
76 | wg.Done()
77 | }(enumFile)
78 | }
79 |
80 | // 生成自定义Class定义文件
81 | extFieldClassFiles := env.GetExtFieldClassFiles()
82 | wg.Add(len(extFieldClassFiles))
83 | for n, e := range extFieldClassFiles {
84 | go func(fileName string, extFieldClasses []*field_type.TableFieldType) {
85 | GenCSBinCodeExtClass(fileName, extFieldClasses, csBinRule, csBinRule.CodeTempDir)
86 | wg.Done()
87 | }(n, e)
88 | }
89 | // 等待完成
90 | wg.Wait()
91 |
92 | //清空目录
93 | if err := fanpath.InitDirAndClearFile(fanpath.RelExecuteDir(csBinRule.DataBinDir), `^.*?\.bytes$`); err != nil {
94 | slog.Fatal(err)
95 | }
96 |
97 | if err := fanpath.InitDirAndClearFile(fanpath.RelExecuteDir(csBinRule.GenCodeDir), `^.*?\.cs$`); err != nil {
98 | slog.Fatal(err)
99 | }
100 |
101 | // 拷贝到最终的目录去
102 | err := fanpath.CopyDir(csBinRule.DataTempDir, csBinRule.DataBinDir)
103 | if err != nil {
104 | slog.Fatal("Copy files error:" + err.Error())
105 | }
106 | err = fanpath.CopyDir(csBinRule.CodeTempDir, csBinRule.GenCodeDir)
107 | if err != nil {
108 | slog.Fatal("Copy files error:" + err.Error())
109 | }
110 | }
111 |
112 | func GenCSBinData(dataModel *model.TableModel, outputPath string) {
113 | byteBuff := serialization.NewByteBuf(1024)
114 | binary := visitor.NewBinary(byteBuff)
115 | binary.AcceptTable(dataModel)
116 | bytes := byteBuff.GetBytes()
117 | filePath := fanpath.RelExecuteDir(outputPath, dataModel.Meta.Target+".bytes")
118 | file, err := os.Create(filePath)
119 | if err != nil {
120 | slog.Fatalf("export cs_bin bytes file create file error:%v", err)
121 | }
122 | _, err = file.Write(bytes)
123 | if err != nil {
124 | slog.Fatalf("export cs_bin bytes file write error:%v", err)
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # table-export
2 |
3 | ## 介绍
4 | table-export 是一个高性能游戏导表工具,使用golang编写,目前支持源数据excel和csv,导出格式支持
5 |
6 | C# binary (推荐),lua (推荐),json,可以根据自己的需求自己修改扩展。
7 |
8 | ## [B站介绍视频](https://www.bilibili.com/video/BV1ru411871G/)
9 |
10 | ## 核心特性
11 |
12 | - 高性能,方便扩展
13 | - 支持集成进lua热更项目、C# HybridCLR热更项目
14 | - 使用toml配置来定义导出规则
15 | - 支持多字段索引id,例如多个字段才确定这行数据的唯一
16 | - 导出的C# binary,以及lua,都是支持内存和硬盘占用优化,这是对比其他导表工具的最大优势。table-export会在导出的时候把相同重复的引用数据只导出一份,并且在runtime读取的时候,只读取一份,然后其他和这份数据相同的都会持有这个引用,实际上游戏数据表越多数据量越大的时候,能够优化的存储和内存将近一半,这样也同时也加快了游戏启动的加载的时间
17 | - 方便的数据检测(cs_bin模式)
18 | - 自定义复合类型,使用go或者js脚本扩展(cs_bin模式)
19 | - 自定义枚举(cs_bin模式)
20 | - 单表后处理和全局后处理的js脚本支持(cs_bin模式)
21 |
22 | ## 使用方法
23 |
24 | ### 准备环境
25 |
26 | 1. 安装golang,编译可执行文件。或者从release里下载可执行文件
27 | 2. 把可执行文件放到examples目录下
28 | 3. 工具的配置文件目录为可执行文件的相对路径的./conf/config.toml,examples里已经有一份示例配置
29 | 4. 先在config.toml里面配置好导出规则的配置,例如c# binary
30 |
31 | ```toml
32 | [[meta.rules]]
33 | rule_name = "cs_bin"
34 | config_dir = "./cs_bin_meta"
35 | [meta.rules.cs_bin]
36 | code_temp_dir = "./temp_work/temp_cs_bin_code"
37 | data_temp_dir = "./temp_work/temp_cs_bin_data"
38 | gen_code_dir = "./projects/proj_cs_bin/src/gen"
39 | data_bin_dir = "./projects/proj_cs_bin/table_bytes"
40 | #toml支持多行文本
41 | gen_code_head = """using Serialization;
42 | """
43 | code_namespace = "CfgTable"
44 | # 以下是生成的优化相关数据
45 | gen_optimize = true
46 | ```
47 |
48 | 5. 上述配置中config_dir对应的目录是表的描述文件,一般其他的导表工具会定义在excel头部,但是table-export会使用单独的meta配置来控制表的导出规则等。这样可以在由程序来控制配置表如何输出,而不用让策划帮忙改表的定义,减少了这方面的沟通。
49 |
50 | ### 开始导表
51 |
52 | table_export.exe -m 模式1|模式2|...
53 |
54 | 例如: `table_export.exe -m cs_bin`
55 |
56 | 可以同时导出多种
57 |
58 | 例如 : `table_export.exe -m lua|cs_bin`
59 |
60 | ### **表meta配置文件**
61 |
62 | #### 介绍
63 |
64 | meta配置指一个表的导出规则,控制哪些字段导出,字段类型,字段注释,索引的key等规则,例如以下就是对应base_test表的导出规则
65 |
66 | ```toml
67 | # 目标名字
68 | target = "base_test"
69 | mode = ""
70 | source_type = "excel"
71 |
72 | sources = [
73 | { file_name = "base_test.xlsx", sheet_name = "Sheet1" },
74 | ]
75 |
76 |
77 | fields = [
78 | { active = true, sname = "id" , tname = "id" , type = "int" , key = 1, desc = "主id"},
79 | { active = true, sname = "name" , tname = "name" , type = "string", key = 0, desc = "名字"},
80 | { active = true, sname = "age" , tname = "age" , type = "int" , key = 0, desc = "年龄"},
81 | { active = true, sname = "course" , tname = "course" , type = "[]int" , key = 0, desc = "学科"},
82 | ]
83 |
84 | ```
85 |
86 | #### 生成meta模板
87 |
88 | > table-export提供了根据配置好的excel文件生成一份meta模板的配置,然后在这个基础上做修改,而不是从零自己去写这个配置,以下就是生成模板meta配置的方法
89 |
90 | 先把excel文件放在config.toml中定义src_dir对应的目录下,当前示例定义的目录是examples/excels
91 |
92 | excel 生成meta模板,例如: `table_export -g 目标名字,excel文件名字带后缀,Sheet名`
93 |
94 | ```shell
95 | table_export -g complex_test,complex_test.xlsx,Sheet名
96 | ```
97 |
98 | csv 生成meta模板同理excel,只是参数变成了两个
99 |
100 | ```shell
101 | table_export -g csv_test,csv_test.csv
102 | ```
103 |
104 | #### 使用该meta模板配置,并修改
105 |
106 | 生成之后会在config.toml里的gen_dir配置项中,示例配置定义的目录为examples/temp_meta里,使用上述生成meta模板文件的命令之后,会在这个目录看到文件,需要把生成的文件放到定义的导出规则的目录中去。例如config.toml里的cs_bin的配置中的config_dir字段定义的C#的二进制的导表读取的meta目录为examples/cs_bin_meta里,这样就放到这个目录中。
107 |
108 | 然后打开这个toml配置,把fields中需要导出的字段的active设置为true,并且填入type,以及在这个表中至少定义一个索引key,可以多个但是必须是连续的,例如 key的字段有 1 ,2。这样就会生成一个双key的数据,之后就是双key的数据。例子complex_test就是双key的例子
109 |
110 |
111 | ## Benchmark
112 |
113 | 使用cs_bin模式,用examples中的big_data作为benchmark的示例
114 |
115 | **关闭优化时**
116 |
117 | 
118 |
119 | **开启优化后**
120 |
121 | 
122 |
123 | **可以看到读取时间以及内存占用都大幅降低了**
124 |
125 | # TODO
126 |
127 | 目前1.1以上版本中已经全部支持,后续会更新强大新特性的使用文档
128 |
129 | ## Credits
130 |
131 | - [LuaTableOptimizer](https://github.com/lujian101/LuaTableOptimizer) 使用了lua表优化工具,在此基础上做了更进一步的优化
132 | - [luban](https://github.com/focus-creative-games/luban) 使用了二进制序列化的代码和一些思路
133 | - [goja](https://github.com/dop251/goja) js的脚本引擎,使用js来扩展自定义类型和表后处理
--------------------------------------------------------------------------------
/convert/wrap/entry.go:
--------------------------------------------------------------------------------
1 | package wrap
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/apiconvert"
8 | "github.com/821869798/table-export/field_type"
9 | )
10 |
11 | var valueWrapMap map[field_type.EFieldType]IValueWrap
12 |
13 | func init() {
14 | valueWrapMap = make(map[field_type.EFieldType]IValueWrap)
15 | valueWrapMap[field_type.EFieldType_Int] = &intWrap{}
16 | valueWrapMap[field_type.EFieldType_UInt] = &uintWrap{}
17 | valueWrapMap[field_type.EFieldType_Long] = &longWrap{}
18 | valueWrapMap[field_type.EFieldType_ULong] = &ulongWrap{}
19 | valueWrapMap[field_type.EFieldType_Bool] = &boolWrap{}
20 | valueWrapMap[field_type.EFieldType_Float] = &floatWrap{}
21 | valueWrapMap[field_type.EFieldType_Double] = &doubleWrap{}
22 | valueWrapMap[field_type.EFieldType_String] = &stringWrap{}
23 | valueWrapMap[field_type.EFieldType_Slice] = &sliceWrap{}
24 | valueWrapMap[field_type.EFieldType_Map] = &mapWrap{}
25 | valueWrapMap[field_type.EFieldType_Enum] = &enumWrap{}
26 | valueWrapMap[field_type.EFieldType_Class] = &classWrap{}
27 | }
28 |
29 | // GetOutputValue 获取真实的值,使用interface{}包装
30 | func GetOutputValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (interface{}, error) {
31 | if fieldType.ExtFieldType != nil {
32 | return fieldType.ExtFieldType.ParseOriginData(origin)
33 | }
34 | wrap, ok := valueWrapMap[fieldType.Type]
35 | if !ok {
36 | return nil, errors.New(fmt.Sprintf("GetOutputValue no support field_type type[%v]", fieldType.Type))
37 | }
38 | result, err := wrap.OutputValue(exportType, fieldType, origin)
39 | return result, err
40 | }
41 |
42 | // GetOutputStringValue 获取该导出类型下String的值
43 | func GetOutputStringValue(exportType config.ExportType, fieldType *field_type.TableFieldType, origin string) (string, error) {
44 | wrap, ok := valueWrapMap[fieldType.Type]
45 | if !ok {
46 | return "", errors.New(fmt.Sprintf("GetOutputStringValue no support field_type type[%v]", fieldType.Type))
47 | }
48 | result, err := wrap.OutputStringValue(exportType, fieldType, origin)
49 | return result, err
50 | }
51 |
52 | // GetOutputDefTypeValue 获取这种类型的导出的定义符号·
53 | func GetOutputDefTypeValue(exportType config.ExportType, fieldType *field_type.TableFieldType, collectionReadonly bool) (string, error) {
54 | wrap, ok := valueWrapMap[fieldType.Type]
55 | if !ok {
56 | return "", errors.New(fmt.Sprintf("GetOutputDefTypeValue no support field_type type[%v]", fieldType.Type))
57 | }
58 | result, err := wrap.OutputDefTypeValue(exportType, fieldType, collectionReadonly)
59 | return result, err
60 | }
61 |
62 | func GetFormatValueInterface(fieldType *field_type.TableFieldType, origin interface{}) (interface{}, error) {
63 | wrap, ok := valueWrapMap[fieldType.Type]
64 | if !ok {
65 | return nil, errors.New(fmt.Sprintf("FormatValueInterface no support field_type type[%v]", fieldType.Type))
66 | }
67 | result, err := wrap.FormatValueInterface(fieldType, origin)
68 | return result, err
69 | }
70 |
71 | func RunDataVisitorString(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin string) error {
72 | if fieldType.ExtFieldType != nil {
73 | data, err := fieldType.ExtFieldType.ParseOriginData(origin)
74 | if err != nil {
75 | return err
76 | }
77 | return RunDataVisitorValue(visitor, fieldType, data)
78 | }
79 | wrap, ok := valueWrapMap[fieldType.Type]
80 | if !ok {
81 | return errors.New(fmt.Sprintf("DataVisitorString no support field_type type[%v]", fieldType.Type))
82 | }
83 | err := wrap.DataVisitorString(visitor, fieldType, origin)
84 | return err
85 | }
86 |
87 | func RunDataVisitorValue(visitor apiconvert.IDataVisitor, fieldType *field_type.TableFieldType, origin interface{}) error {
88 | wrap, ok := valueWrapMap[fieldType.Type]
89 | if !ok {
90 | return errors.New(fmt.Sprintf("DataVisitorString no support field_type type[%v]", fieldType.Type))
91 | }
92 | err := wrap.DataVisitorValue(visitor, fieldType, origin)
93 | return err
94 | }
95 |
96 | func GetCodePrintValue(print apiconvert.ICodePrinter, fieldType *field_type.TableFieldType, fieldName string, reader string, depth int32) string {
97 | wrap, ok := valueWrapMap[fieldType.Type]
98 | if !ok {
99 | return ""
100 | }
101 | result := wrap.CodePrintValue(print, fieldType, fieldName, reader, depth)
102 | return result
103 | }
104 |
--------------------------------------------------------------------------------
/export/cs_bin/template_test.go:
--------------------------------------------------------------------------------
1 | package cs_bin
2 |
3 | // 废弃
4 | const templateCSCodeNew = `
5 | {{.CodeHead}}
6 | using System.Collections.Generic;
7 |
8 | namespace {{.NameSpace}}
9 | {
10 | public partial class {{.TableClassName}}
11 | {
12 | private readonly {{UniqueMapDef}} _dataMap;
13 | private readonly List<{{RecordClassName}}> _dataList;
14 | {{~if has_unique~}}
15 |
16 | ///
17 | /// Multiple unique key map,easy to traverse
18 | ///
19 | private readonly {{unique_map_def_rw}} _uniqueDataMap;
20 | {{~end~}}
21 |
22 | public {{.TableClassName}}(ByteBuf _buf)
23 | {
24 | //first read common data
25 | {{tb_common_name}} _commonData = null;
26 | var commonSize = _buf.ReadSize();
27 | if( commonSize > 0)
28 | {
29 | _commonData = new {{tb_common_name}}(_buf);
30 | }
31 |
32 | //then read row data
33 | var size = _buf.ReadSize();
34 | _dataMap = new Dictionary<{{major_key_type}}, {{RecordClassName}}>(size * 3 / 2);
35 | _dataList = new List<{{RecordClassName}}>(size);
36 | {{~if has_unique~}}
37 | _uniqueDataMap = new {{unique_map_def_rw}}(16);
38 | {{~end~}}
39 |
40 | for (int i = 0; i < size; i++)
41 | {
42 | {{RecordClassName}} _v;
43 | _v = {{RecordClassName}}.Deserialize{{RecordClassName}}(_buf, _commonData);
44 | _dataList.Add(_v);
45 | _dataMap.Add(_v.{{major_key_name}}, _v);
46 | {{~if has_unique~}}
47 | {{cs_unique_key_map_assignment unique_keys '_uniqueDataMap' '_v' RecordClassName}}
48 | {{~end~}}
49 | }
50 |
51 | PostInit();
52 | }
53 |
54 | public {{def_map}} DataMap => _dataMap;
55 | public {{def_list}}<{{.RecordClassName}}> DataList => _dataList;
56 |
57 | public {{.RecordClassName}} GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null;
58 | public {{.RecordClassName}} Get(int key) => _dataMap[key];
59 | public {{.RecordClassName}} this[int key] => _dataMap[key];
60 | {{~if has_unique > 0~}}
61 |
62 | public {{unique_map_def_rw}} UniqueKeyDataMap => _uniqueDataMap;
63 | {{cs_unique_key_map_get_func unique_keys '_uniqueDataMap' .RecordClassName}}
64 | {{~end~}}
65 |
66 | ///
67 | /// post process table
68 | ///
69 | partial void PostInit();
70 |
71 | }
72 |
73 | public partial class {{.RecordClassName}}
74 | {
75 | private {{.RecordClassName}}(ByteBuf _buf, {{tb_common_name}} _commonData)
76 | {
77 | {{~ for field in fields~}}
78 | {{cs_bin_field_reader_ex field optimize '_buf' '_commonData._field'}}
79 | {{~end~}}
80 |
81 | PostInit();
82 | }
83 |
84 | internal static {{.RecordClassName}} Deserialize{{.RecordClassName}}(ByteBuf _buf, {{tb_common_name}} _commonData)
85 | {
86 | return new {{.RecordClassName}}(_buf, _commonData);
87 | }
88 |
89 | ///
90 | /// major key
91 | ///
92 | public {{major_key_type}} {{major_key_name}} { get; private set; }
93 |
94 | {{~ for field in fields offset:1~}}
95 | ///
96 | /// {{field.desc}}
97 | ///
98 | public {{cs_get_class_field_def field.type collection_readonly}} {{field.name}} { get; private set; }
99 | {{~end~}}
100 |
101 | ///
102 | /// post process table
103 | ///
104 | partial void PostInit();
105 | }
106 |
107 | ///
108 | /// internal common data optimize
109 | ///
110 | internal class {{TableCommonName}}
111 | {
112 |
113 | {{~if optimize_fields != null ~}}
114 | {{~ for opt_field in optimize_fields ~}}
115 | internal {{cs_get_class_field_def opt_field.optimize_type collection_readonly}} _field{{for.index}} { get; private set; }
116 | {{~end~}}
117 | {{~end~}}
118 | internal {{TableCommonName}}(ByteBuf _buf)
119 | {
120 | {{~ for opt_field in optimize_fields~}}
121 | {{cs_bin_field_reader opt_field.optimize_type '_buf' ('_field' + for.index)}}
122 | {{~end~}}
123 | }
124 |
125 | }
126 | }
127 | `
128 |
--------------------------------------------------------------------------------
/meta/table_meta.go:
--------------------------------------------------------------------------------
1 | package meta
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/field_type"
7 | "github.com/gookit/slog"
8 | )
9 |
10 | type TableMeta struct {
11 | Target string
12 | Mode string
13 | SourceType string
14 | Sources []*TableSource
15 | Fields []*TableField
16 | Keys []*TableField //关键key
17 | SourceMap map[string]bool //需要的source字段名
18 | RecordChecks []*TableCheck // 针对一行数据的检查
19 | GlobalChecks []*TableCheck // 只调一次的Check
20 | PostScript string
21 | ExtraFields []*TableField // 额外字段
22 | extraFieldMap map[string]bool
23 | }
24 |
25 | type TableSource struct {
26 | Table string
27 | Sheet string
28 | }
29 |
30 | func NewTableMeta(rtm *RawTableMeta) (*TableMeta, error) {
31 | t := &TableMeta{
32 | Target: rtm.Target,
33 | Mode: rtm.Mode,
34 | SourceType: rtm.SourceType,
35 | SourceMap: make(map[string]bool),
36 | PostScript: rtm.PostScript,
37 | extraFieldMap: make(map[string]bool),
38 | }
39 |
40 | //source
41 | sourceLen := len(rtm.Sources)
42 | if sourceLen == 0 {
43 | return nil, errors.New(fmt.Sprintf("table meta config[%v] sources count must >= 0", rtm.Target))
44 | }
45 | t.Sources = make([]*TableSource, sourceLen)
46 | for index, source := range rtm.Sources {
47 | t.Sources[index] = &TableSource{
48 | Table: source.Table,
49 | Sheet: source.Sheet,
50 | }
51 | }
52 |
53 | //校验
54 | fields := make([]*TableField, 0)
55 | keysMap := make(map[int]*TableField)
56 | for _, rtf := range rtm.Fields {
57 | if !rtf.Active {
58 | continue
59 | }
60 |
61 | tft, err := newTableField(rtf)
62 |
63 | if err != nil {
64 | return nil, err
65 | }
66 | fields = append(fields, tft)
67 |
68 | if tft.Key > 0 {
69 | //同一个key不能重复
70 | if _, ok := keysMap[tft.Key]; ok {
71 | return nil, errors.New(fmt.Sprintf("table meta config[%v] key value repeat[%v]", rtm.Target, tft.Key))
72 | }
73 | //key只支持基础类型
74 | if !tft.Type.IsBaseType() {
75 | return nil, errors.New(fmt.Sprintf("table meta config[%v] key only support base type[%v]", rtm.Target, tft.Target))
76 | }
77 | keysMap[tft.Key] = tft
78 |
79 | }
80 | }
81 |
82 | if len(fields) == 0 {
83 | return nil, errors.New(fmt.Sprintf("table meta config[%v] not one active field_type", rtm.Target))
84 | }
85 |
86 | //检车key是否合法
87 | keyCount := len(keysMap)
88 | if keyCount == 0 {
89 | return nil, errors.New(fmt.Sprintf("table meta config[%v] key count must >= 0", rtm.Target))
90 | }
91 | keys := make([]*TableField, keyCount)
92 | for keyIndex, tft := range keysMap {
93 | if keyIndex > keyCount {
94 | return nil, errors.New(fmt.Sprintf("table meta config[%v] key value error[%v]", rtm.Target, keyIndex))
95 | }
96 | keys[keyIndex-1] = tft
97 | }
98 |
99 | t.Fields = fields
100 | t.Keys = keys
101 |
102 | //判断Source是否重复,并且生成需要原始字段的map
103 | filedMap := make(map[string]*TableField)
104 | for _, tf := range fields {
105 | if _, ok := filedMap[tf.Target]; ok {
106 | return nil, errors.New(fmt.Sprintf("table meta config[%v] target name repeated[%v]", rtm.Target, tf.Target))
107 | }
108 | filedMap[tf.Target] = tf
109 | t.SourceMap[tf.Source] = true
110 | }
111 |
112 | // 生成Check
113 | for _, rck := range rtm.Checks {
114 | if !rck.Active {
115 | continue
116 | }
117 | tck := newTableCheck(rck)
118 | if rck.Global {
119 | t.GlobalChecks = append(t.GlobalChecks, tck)
120 | } else {
121 | t.RecordChecks = append(t.RecordChecks, tck)
122 | }
123 | }
124 |
125 | return t, nil
126 | }
127 |
128 | func (tm *TableMeta) NotKeyFieldCount() int {
129 | return len(tm.Fields) - len(tm.Keys)
130 | }
131 |
132 | func (tm *TableMeta) GetKeyDefType(finalType field_type.EFieldType) *field_type.TableFieldType {
133 | return tm.GetKeyDefTypeOffset(finalType, 0)
134 | }
135 |
136 | func (tm *TableMeta) GetKeyDefTypeOffset(finalType field_type.EFieldType, offset int) *field_type.TableFieldType {
137 | value := field_type.NewTableFieldType(finalType)
138 | for i := len(tm.Keys) - 1; i >= offset; i-- {
139 | key := tm.Keys[i]
140 | value = field_type.NewTableFieldMapType(key.Type, value)
141 | }
142 | return value
143 | }
144 |
145 | func (tm *TableMeta) AddExtraField(name string, value *field_type.TableFieldType, desc string) {
146 | if value == nil {
147 | slog.Fatalf("table meta config[%v] extra field type is nil", tm.Target)
148 | }
149 | _, ok := tm.extraFieldMap[name]
150 | if ok {
151 | slog.Fatalf("table meta config[%v] extra field name repeated[%v]", tm.Target, name)
152 | return
153 | }
154 |
155 | tm.extraFieldMap[name] = true
156 | tm.ExtraFields = append(tm.ExtraFields, &TableField{
157 | Target: name,
158 | Type: value,
159 | Desc: desc,
160 | })
161 | }
162 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/src/gen/TblGameConfig.cs:
--------------------------------------------------------------------------------
1 | using Serialization;
2 |
3 | using System.Collections.Generic;
4 |
5 | namespace CfgTable
6 | {
7 |
8 | public partial class TblGameConfig
9 | {
10 |
11 | private readonly Dictionary _dataMap;
12 | private readonly List _dataList;
13 |
14 |
15 | public TblGameConfig(ByteBuf _buf)
16 | {
17 | //first read common data
18 | _TbCommonGameConfig _commonData = null;
19 | var commonSize = _buf.ReadSize();
20 | if( commonSize > 0)
21 | {
22 | _commonData = new _TbCommonGameConfig(_buf);
23 | }
24 |
25 | var size = _buf.ReadSize();
26 | _dataMap = new Dictionary(16);
27 | _dataList = new List(size);
28 |
29 | for (int i = 0; i < size; i++)
30 | {
31 | CfgGameConfig _v;
32 | _v = CfgGameConfig.DeserializeCfgGameConfig(_buf, _commonData);
33 | _dataList.Add(_v);
34 | _dataMap[_v.id] = _v;
35 | }
36 |
37 | heroLevelMax = _buf.ReadInt();
38 | {int __n0 = _buf.ReadSize(); heroInitTeam = new int[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ int __v0; __v0 = _buf.ReadInt(); heroInitTeam[__i0] = __v0; } }
39 | initItems = new KVList_IntInt(_buf);
40 | openDebug = _buf.ReadBool();
41 |
42 | PostInit();
43 | }
44 |
45 | public int DataCount => _dataList.Count;
46 | public Dictionary DataMap => _dataMap;
47 | public List DataList => _dataList;
48 |
49 | public CfgGameConfig Get(int __k0)
50 | {
51 | if (_dataMap.TryGetValue(__k0, out var __tmpv0)) { return __tmpv0; }
52 | #if UNITY_EDITOR
53 | Debug.LogError($"[TblGameConfig] config id not found,id:{__k0.ToString()}");
54 | #endif
55 | return null;
56 | }
57 |
58 | public CfgGameConfig GetWithoutError(int __k0)
59 | {
60 | if (_dataMap.TryGetValue(__k0, out var __tmpv0)) { return __tmpv0; }
61 | return null;
62 | }
63 |
64 |
65 | ///
66 | ///
67 | ///
68 | public int heroLevelMax { get; private set; }
69 |
70 | ///
71 | ///
72 | ///
73 | public int[] heroInitTeam { get; private set; }
74 |
75 | ///
76 | ///
77 | ///
78 | public KVList_IntInt initItems { get; private set; }
79 |
80 | ///
81 | ///
82 | ///
83 | public bool openDebug { get; private set; }
84 |
85 | ///
86 | /// table data file
87 | ///
88 | public static string TableFileName { get; } = "GameConfig";
89 |
90 | ///
91 | /// post process table
92 | ///
93 | partial void PostInit();
94 |
95 | }
96 |
97 | public partial class CfgGameConfig
98 | {
99 | private CfgGameConfig(ByteBuf _buf, _TbCommonGameConfig _commonData)
100 | {
101 |
102 | id = _buf.ReadInt();
103 | { int dataIndex = _buf.ReadInt() - 1; data = _commonData._field0[dataIndex]; }
104 | { int dataIndex = _buf.ReadInt() - 1; 备注 = _commonData._field1[dataIndex]; }
105 | PostInit();
106 | }
107 |
108 | internal static CfgGameConfig DeserializeCfgGameConfig(ByteBuf _buf, _TbCommonGameConfig _commonData)
109 | {
110 | return new CfgGameConfig(_buf, _commonData);
111 | }
112 |
113 | ///
114 | /// id
115 | ///
116 | public int id { get; private set; }
117 |
118 | ///
119 | /// 内容值
120 | ///
121 | public string data { get; private set; }
122 |
123 | ///
124 | /// 描述
125 | ///
126 | public string 备注 { get; private set; }
127 |
128 |
129 | ///
130 | /// post process table
131 | ///
132 | partial void PostInit();
133 | }
134 |
135 | ///
136 | /// internal common data optimize
137 | ///
138 | internal class _TbCommonGameConfig
139 | {
140 |
141 | internal string[] _field0 { get; private set; }
142 | internal string[] _field1 { get; private set; }
143 | internal _TbCommonGameConfig(ByteBuf _buf)
144 | {
145 |
146 | {int __n0 = _buf.ReadSize(); _field0 = new string[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ string __v0; __v0 = _buf.ReadString(); _field0[__i0] = __v0; } }
147 | {int __n0 = _buf.ReadSize(); _field1 = new string[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ string __v0; __v0 = _buf.ReadString(); _field1[__i0] = __v0; } }
148 | }
149 |
150 | }
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/export/cs_bin/template_cs_bin.go:
--------------------------------------------------------------------------------
1 | package cs_bin
2 |
3 | const templateCSCodeTable string = `{{ .CodeHead }}
4 | using System.Collections.Generic;
5 |
6 | namespace {{.NameSpace}}
7 | {
8 |
9 | public partial class {{.TableClassName}}
10 | {
11 |
12 | private readonly {{.KeyDefTypeMap}} _dataMap;
13 | private readonly List<{{.RecordClassName}}> _dataList;
14 |
15 |
16 | public {{.TableClassName}}(ByteBuf _buf)
17 | {
18 | //first read common data
19 | {{.TableCommonName}} _commonData = null;
20 | var commonSize = _buf.ReadSize();
21 | if( commonSize > 0)
22 | {
23 | _commonData = new {{.TableCommonName}}(_buf);
24 | }
25 |
26 | var size = _buf.ReadSize();
27 | _dataMap = new {{.KeyDefTypeMap}}(16);
28 | _dataList = new List<{{.RecordClassName}}>(size);
29 |
30 | for (int i = 0; i < size; i++)
31 | {
32 | {{.RecordClassName}} _v;
33 | _v = {{.RecordClassName}}.Deserialize{{.RecordClassName}}(_buf, _commonData);
34 | _dataList.Add(_v);
35 | {{UniqueMapAssignment $ "_dataMap" "_v" 4}}
36 | }
37 | {{ range $v := .CSCodeExtraFields }}
38 | {{CSbinFieldReader $v.Field.Type $v.Name "_buf" }}
39 | {{- end }}
40 |
41 | PostInit();
42 | }
43 |
44 | public int DataCount => _dataList.Count;
45 | public {{.KeyDefTypeMap}} DataMap => _dataMap;
46 | public List<{{.RecordClassName}}> DataList => _dataList;
47 |
48 | {{UniqueMapGetFunc $ "_dataMap" 2}}
49 |
50 | {{UniqueMapGetFuncWithoutError $ "_dataMap" 2}}
51 |
52 | {{ range $v := .CSCodeExtraFields }}
53 | ///
54 | /// {{$v.Desc}}
55 | ///
56 | public {{$v.TypeDef}} {{$v.Name}} { get; private set; }
57 | {{ end }}
58 | ///
59 | /// table data file
60 | ///
61 | public static string TableFileName { get; } = "{{.TableName}}";
62 |
63 | ///
64 | /// post process table
65 | ///
66 | partial void PostInit();
67 |
68 | }
69 |
70 | public partial class {{.RecordClassName}}
71 | {
72 | private {{.RecordClassName}}(ByteBuf _buf, {{.TableCommonName}} _commonData)
73 | {
74 | {{ range $v := .CSCodeWriteFields }}
75 | {{CSbinFieldReaderEx $v "_buf" "_commonData._field"}}
76 | {{- end }}
77 | PostInit();
78 | }
79 |
80 | internal static {{.RecordClassName}} Deserialize{{.RecordClassName}}(ByteBuf _buf, {{.TableCommonName}} _commonData)
81 | {
82 | return new {{.RecordClassName}}(_buf, _commonData);
83 | }
84 | {{ range $v := .CSCodeWriteFields }}
85 | ///
86 | /// {{$v.Desc}}
87 | ///
88 | public {{$v.TypeDef}} {{$v.Name}} { get; private set; }
89 | {{ end }}
90 |
91 | ///
92 | /// post process table
93 | ///
94 | partial void PostInit();
95 | }
96 |
97 | ///
98 | /// internal common data optimize
99 | ///
100 | internal class {{.TableCommonName}}
101 | {
102 | {{ if .TableOptimize -}}
103 | {{ range $index,$v := .TableOptimize.OptimizeFields }}
104 | internal {{GetOutputDefTypeValue $v.OptimizeType $.CollectionReadonly}} _field{{$index}} { get; private set; }{{ end }}
105 | {{- end }}
106 | internal {{.TableCommonName}}(ByteBuf _buf)
107 | {
108 | {{ if .TableOptimize -}}
109 | {{ range $index,$v := .TableOptimize.OptimizeFields }}
110 | {{ $combine := (printf "_field%v" $index) }}{{CSbinFieldReader $v.OptimizeType $combine "_buf"}}{{ end }}
111 | {{- end }}
112 | }
113 |
114 | }
115 |
116 | }
117 | `
118 |
119 | const templateCSCodeEnum = `
120 | namespace {{.NameSpace}}
121 | {
122 | {{ range $v := .Enums }}
123 | public enum {{$.EnumDefinePrefix}}{{$v.Name}}
124 | {
125 | {{- range $enumValue := $v.Values }}
126 | // {{$enumValue.Name}} : {{$enumValue.Desc}}
127 | {{$enumValue.Name}} = {{$enumValue.Index}},
128 | {{- end }}
129 | }
130 | {{ end }}
131 | }
132 | `
133 |
134 | const templateCSCodeClass = `{{ .CodeHead }}
135 | using System.Collections.Generic;
136 |
137 | namespace {{.NameSpace}}
138 | {
139 | {{ range $classType := .ClassTypes }}
140 | public partial class {{$.ClassDefinePrefix}}{{$classType.Name}}
141 | {
142 | {{- range $v := $classType.Fields }}
143 | public {{$v.TypeDef}} {{$v.Name}} { get; private set; }
144 | {{- end }}
145 |
146 | public {{$.ClassDefinePrefix}}{{$classType.Name}}(ByteBuf _buf)
147 | {
148 | {{- range $v := $classType.Fields }}
149 | {{CSbinFieldReader $v.FieldType $v.Name "_buf" }}
150 | {{- end }}
151 | PostInit();
152 | }
153 |
154 | ///
155 | /// post process ext class
156 | ///
157 | partial void PostInit();
158 | }
159 | {{ end }}
160 | }
161 | `
162 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/src/gen/Tblbig_data.cs:
--------------------------------------------------------------------------------
1 | using Serialization;
2 |
3 | using System.Collections.Generic;
4 |
5 | namespace CfgTable
6 | {
7 |
8 | public partial class Tblbig_data
9 | {
10 |
11 | private readonly Dictionary _dataMap;
12 | private readonly List _dataList;
13 |
14 |
15 | public Tblbig_data(ByteBuf _buf)
16 | {
17 | //first read common data
18 | _TbCommonbig_data _commonData = null;
19 | var commonSize = _buf.ReadSize();
20 | if( commonSize > 0)
21 | {
22 | _commonData = new _TbCommonbig_data(_buf);
23 | }
24 |
25 | var size = _buf.ReadSize();
26 | _dataMap = new Dictionary(16);
27 | _dataList = new List(size);
28 |
29 | for (int i = 0; i < size; i++)
30 | {
31 | Cfgbig_data _v;
32 | _v = Cfgbig_data.DeserializeCfgbig_data(_buf, _commonData);
33 | _dataList.Add(_v);
34 | _dataMap[_v.id] = _v;
35 | }
36 |
37 |
38 | PostInit();
39 | }
40 |
41 | public int DataCount => _dataList.Count;
42 | public Dictionary DataMap => _dataMap;
43 | public List DataList => _dataList;
44 |
45 | public Cfgbig_data Get(int __k0)
46 | {
47 | if (_dataMap.TryGetValue(__k0, out var __tmpv0)) { return __tmpv0; }
48 | #if UNITY_EDITOR
49 | Debug.LogError($"[Tblbig_data] config id not found,id:{__k0.ToString()}");
50 | #endif
51 | return null;
52 | }
53 |
54 | public Cfgbig_data GetWithoutError(int __k0)
55 | {
56 | if (_dataMap.TryGetValue(__k0, out var __tmpv0)) { return __tmpv0; }
57 | return null;
58 | }
59 |
60 |
61 | ///
62 | /// table data file
63 | ///
64 | public static string TableFileName { get; } = "big_data";
65 |
66 | ///
67 | /// post process table
68 | ///
69 | partial void PostInit();
70 |
71 | }
72 |
73 | public partial class Cfgbig_data
74 | {
75 | private Cfgbig_data(ByteBuf _buf, _TbCommonbig_data _commonData)
76 | {
77 |
78 | id = _buf.ReadInt();
79 | { int dataIndex = _buf.ReadInt() - 1; name = _commonData._field0[dataIndex]; }
80 | age = _buf.ReadInt();
81 | { int dataIndex = _buf.ReadInt() - 1; course = _commonData._field1[dataIndex]; }
82 | { int dataIndex = _buf.ReadInt() - 1; score = _commonData._field2[dataIndex]; }
83 | PostInit();
84 | }
85 |
86 | internal static Cfgbig_data DeserializeCfgbig_data(ByteBuf _buf, _TbCommonbig_data _commonData)
87 | {
88 | return new Cfgbig_data(_buf, _commonData);
89 | }
90 |
91 | ///
92 | /// id
93 | ///
94 | public int id { get; private set; }
95 |
96 | ///
97 | /// 名字
98 | ///
99 | public string name { get; private set; }
100 |
101 | ///
102 | /// 年龄
103 | ///
104 | public int age { get; private set; }
105 |
106 | ///
107 | /// 学科id
108 | ///
109 | public int[] course { get; private set; }
110 |
111 | ///
112 | /// 成绩组
113 | ///
114 | public Dictionary score { get; private set; }
115 |
116 |
117 | ///
118 | /// post process table
119 | ///
120 | partial void PostInit();
121 | }
122 |
123 | ///
124 | /// internal common data optimize
125 | ///
126 | internal class _TbCommonbig_data
127 | {
128 |
129 | internal string[] _field0 { get; private set; }
130 | internal int[][] _field1 { get; private set; }
131 | internal Dictionary[] _field2 { get; private set; }
132 | internal _TbCommonbig_data(ByteBuf _buf)
133 | {
134 |
135 | {int __n0 = _buf.ReadSize(); _field0 = new string[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ string __v0; __v0 = _buf.ReadString(); _field0[__i0] = __v0; } }
136 | {int __n0 = _buf.ReadSize(); _field1 = new int[__n0][]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ int[] __v0; {int __n1 = _buf.ReadSize(); __v0 = new int[__n1]; for(var __i1 = 0 ; __i1 < __n1 ; __i1++ ){ int __v1; __v1 = _buf.ReadInt(); __v0[__i1] = __v1; } } _field1[__i0] = __v0; } }
137 | {int __n0 = _buf.ReadSize(); _field2 = new Dictionary[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ Dictionary __v0; { int __n1 = _buf.ReadSize(); __v0 = new Dictionary (__n1 * 3 / 2); for(var __i1 = 0 ; __i1 < __n1 ; __i1++ ) {int __k1; __k1 = _buf.ReadInt(); int __v1; __v1 = _buf.ReadInt(); __v0.Add(__k1, __v1); } } _field2[__i0] = __v0; } }
138 | }
139 |
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/export/json/export_json.go:
--------------------------------------------------------------------------------
1 | package json
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/821869798/fankit/fanpath"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/wrap"
8 | "github.com/821869798/table-export/data/model"
9 | "github.com/821869798/table-export/export/api"
10 | "github.com/821869798/table-export/export/common"
11 | "github.com/821869798/table-export/meta"
12 | "github.com/821869798/table-export/util"
13 | "github.com/gookit/slog"
14 | "os"
15 | "path/filepath"
16 | "time"
17 | )
18 |
19 | type ExportJson struct {
20 | tableMetas []*meta.RawTableMeta
21 | }
22 |
23 | func NewExportJson(tableMetas []*meta.RawTableMeta, extraArg map[string]string) api.IExport {
24 | e := &ExportJson{
25 | tableMetas: tableMetas,
26 | }
27 | return e
28 | }
29 |
30 | func (e *ExportJson) TableMetas() []*meta.RawTableMeta {
31 | return e.tableMetas
32 | }
33 |
34 | func (e *ExportJson) Export(ru config.MetaRuleUnit) {
35 |
36 | jsonRule, ok := ru.(*config.RawMetaRuleUnitJson)
37 | if !ok {
38 | slog.Fatal("Export Json expect *RawMetaRuleUnitJson Rule Unit")
39 | }
40 |
41 | outputPath := fanpath.RelExecuteDir(jsonRule.JsonOutputDir)
42 | //清空目录
43 | if err := fanpath.InitDirAndClearFile(outputPath, `^.*?\.json$`); err != nil {
44 | slog.Fatal(err)
45 | }
46 |
47 | defer util.TimeCost(time.Now(), "export json time cost = %v\n")
48 |
49 | //实际开始转换
50 | common.LoadTableModelParallel(e.tableMetas, func(dataModel *model.TableModel) {
51 | ExportJsonFile(dataModel, outputPath)
52 | })
53 |
54 | }
55 |
56 | func ExportJsonFile(dataModel *model.TableModel, outputPath string) {
57 |
58 | writeContent, _ := generateJsonData(dataModel, false)
59 |
60 | exportJson(dataModel, outputPath, writeContent)
61 | }
62 |
63 | func ExportListJsonFile(dataModel *model.TableModel, outputPath string) {
64 | writeContent, writeList := generateJsonData(dataModel, true)
65 |
66 | writeRoot := map[string]interface{}{
67 | "dataMap": writeContent,
68 | "dataList": writeList,
69 | }
70 |
71 | exportJson(dataModel, outputPath, writeRoot)
72 | }
73 |
74 | func generateJsonData(dataModel *model.TableModel, exportList bool) (map[string]interface{}, []interface{}) {
75 | //创建存储的数据结构
76 | writeContent := make(map[string]interface{})
77 | var dataList []interface{} = nil
78 | // 导出的是列表模式
79 | if exportList {
80 | dataList = make([]interface{}, 0)
81 | }
82 | rowDataOffset := config.GlobalConfig.Table.DataStart + 1
83 | for rowIndex, rowData := range dataModel.RawData {
84 | //数据表中一行数据的字符串
85 | recordMap := make(map[string]interface{}, len(dataModel.Meta.Fields))
86 | keys := make([]string, len(dataModel.Meta.Keys))
87 | for _, tf := range dataModel.Meta.Fields {
88 | rawIndex := dataModel.NameIndexMapping[tf.Target]
89 | var rawStr string
90 | if rawIndex < len(rowData) {
91 | rawStr = rowData[rawIndex]
92 | }
93 | output, err := wrap.GetOutputValue(config.ExportType_Json, tf.Type, rawStr)
94 | if err != nil {
95 | slog.Fatalf("export json target file[%v] RowCount[%v] filedName[%v] error:%v", dataModel.Meta.Target, rowIndex+rowDataOffset, tf.Source, err)
96 | }
97 |
98 | recordMap[tf.Target] = output
99 |
100 | //存储key
101 | if tf.Key > 0 {
102 | formatKey, err := wrap.GetOutputStringValue(config.ExportType_Json, tf.Type, rawStr)
103 | if err != nil {
104 | slog.Fatalf("export json target file[%v] RowCount[%v] filedName[%v] format key error:%v", dataModel.Meta.Target, rowIndex+rowDataOffset, tf.Source, err)
105 | }
106 | keys[tf.Key-1] = formatKey
107 | }
108 | }
109 |
110 | //写入key和数据
111 | dataMap := writeContent
112 | for i := 0; i < len(keys)-1; i++ {
113 | keyStr := keys[i]
114 | tmpInterface, ok := dataMap[keyStr]
115 | var tmpMap map[string]interface{}
116 | if !ok {
117 | tmpMap = make(map[string]interface{})
118 | dataMap[keyStr] = tmpMap
119 | } else {
120 | tmpMap = tmpInterface.(map[string]interface{})
121 | }
122 | dataMap = tmpMap
123 | }
124 |
125 | //判断唯一key是否重复
126 | lastKey := keys[len(keys)-1]
127 | _, ok := dataMap[lastKey]
128 | if ok {
129 | slog.Fatalf("export json target file[%v] RowCount[%v] key is repeated:%v", dataModel.Meta.Target, rowIndex+rowDataOffset, keys)
130 | }
131 |
132 | if exportList {
133 | dataMap[lastKey] = len(dataList)
134 | dataList = append(dataList, recordMap)
135 | } else {
136 | dataMap[lastKey] = recordMap
137 | }
138 | }
139 | return writeContent, dataList
140 | }
141 |
142 | func exportJson(dataModel *model.TableModel, outputPath string, writeContent any) {
143 | b, err := json.MarshalIndent(writeContent, "", " ")
144 | if err != nil {
145 | slog.Fatalf("export json error:%v", err)
146 | }
147 |
148 | filePath := filepath.Join(outputPath, dataModel.Meta.Target+".json")
149 |
150 | f, err := os.Create(filePath) //os.OpenFile(destFile, os.O_WRONLY, 0600)
151 | if err != nil {
152 | slog.Fatalf("export json error:%v", err)
153 | }
154 |
155 | _, err = f.Write(b)
156 | if err != nil {
157 | slog.Fatalf("export json error:%v", err)
158 | }
159 |
160 | err = f.Close()
161 | if err != nil {
162 | slog.Fatalf("export json error:%v", err)
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/export/common/export_util.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "github.com/821869798/fankit/fanpath"
5 | "github.com/821869798/table-export/config"
6 | "github.com/821869798/table-export/data/check"
7 | "github.com/821869798/table-export/data/env"
8 | "github.com/821869798/table-export/data/model"
9 | "github.com/821869798/table-export/ext"
10 | "github.com/821869798/table-export/ext/ext_field_script"
11 | "github.com/821869798/table-export/ext/ext_post"
12 | "github.com/821869798/table-export/meta"
13 | "github.com/BurntSushi/toml"
14 | "github.com/bmatcuk/doublestar/v4"
15 | "github.com/gookit/slog"
16 | "os"
17 | "path/filepath"
18 | "strings"
19 | "sync"
20 | )
21 |
22 | func ExportPlusCommon(tableMetas []*meta.RawTableMeta, rulePlus config.MetaRuleUnitPlus) []*model.TableModel {
23 |
24 | // 加载枚举配置
25 | var enumFiles = rulePlus.GetEnumFiles()
26 | rawEnumConfigs := make([]*config.RawMetaEnumConfig, 0, len(enumFiles))
27 | for _, p := range enumFiles {
28 | matches, err := doublestar.FilepathGlob(p)
29 | if err != nil {
30 | slog.Fatalf("Enum Files load error filePath:%s err:%v", p, err)
31 | }
32 | for _, m := range matches {
33 | fullPath := fanpath.AbsOrRelExecutePath(m)
34 | enumConfig := new(config.RawMetaEnumConfig)
35 | if _, err := toml.DecodeFile(fullPath, enumConfig); err != nil {
36 | slog.Fatalf("load enum config error:%v", err)
37 | }
38 | rawEnumConfigs = append(rawEnumConfigs, enumConfig)
39 | }
40 | }
41 | if err := env.AddEnumDefines(rawEnumConfigs); err != nil {
42 | slog.Fatalf("add enum config error:%v", err)
43 | }
44 |
45 | // 添加内置扩展类型
46 | for _, extFieldTypeName := range rulePlus.GetBuiltinFieldTypes() {
47 | extFieldType, ok := ext.GetExistExtFieldType(extFieldTypeName)
48 | if !ok {
49 | slog.Fatalf("no builtin ext field type:%v", extFieldTypeName)
50 | os.Exit(1)
51 | }
52 | err := env.AddExtFieldType(extFieldType)
53 | if err != nil {
54 | slog.Fatalf("add ext field type error:%v", err)
55 | os.Exit(1)
56 | }
57 | }
58 |
59 | // 添加脚本实现的扩展类型
60 | for _, extFieldScriptPath := range rulePlus.GetExtFieldTypeScriptPath() {
61 | matches, err := doublestar.FilepathGlob(extFieldScriptPath)
62 | if err != nil {
63 | slog.Fatalf("Ext Script path glob error filePath:%s err:%v", extFieldScriptPath, err)
64 | os.Exit(1)
65 | }
66 | for _, m := range matches {
67 | fileExt := filepath.Ext(m)
68 | switch fileExt {
69 | case ".js":
70 | extFieldType, err := ext_field_script.NewExtFieldJS(strings.ReplaceAll(m, "\\", "/"))
71 | if err != nil {
72 | slog.Fatalf("Ext Script Files laod error filePath:%s err:%v", m, err)
73 | os.Exit(1)
74 | }
75 | err = env.AddExtFieldType(extFieldType)
76 | if err != nil {
77 | slog.Fatalf("add ext field type error:%v", err)
78 | os.Exit(1)
79 | }
80 | default:
81 | slog.Fatalf("ext field script type not support:%s", fileExt)
82 | }
83 | }
84 | }
85 |
86 | //实际开始转换
87 | allDataModel := LoadTableModelPlusParallel(tableMetas, rulePlus, func(tableModel *model.TableModel) {
88 | // 单表的后处理
89 | scriptPath := strings.TrimSpace(tableModel.Meta.PostScript)
90 | if scriptPath != "" {
91 | fileExt := filepath.Ext(scriptPath)
92 | switch fileExt {
93 | case ".js":
94 | extPostTable, err := ext_post.NewExtPostTableJS(strings.ReplaceAll(scriptPath, "\\", "/"))
95 | if err != nil {
96 | slog.Fatalf("Ext Script Files laod error filePath:%s err:%v", tableModel.Meta.PostScript, err)
97 | os.Exit(1)
98 | }
99 | err = extPostTable.PostTable(tableModel)
100 | if err != nil {
101 | slog.Fatalf("Ext Script post process error filePath:%s err:%v", tableModel.Meta.PostScript, err)
102 | os.Exit(1)
103 | }
104 | default:
105 | slog.Fatalf("ext post script type not support:%s", fileExt)
106 | }
107 |
108 | }
109 |
110 | })
111 |
112 | // 表的数据检查
113 | global := make(map[string]map[interface{}]interface{}, len(allDataModel))
114 | for _, m := range allDataModel {
115 | global[m.Meta.Target] = m.MemTable.RawDataMapping()
116 | }
117 | wgCheck := sync.WaitGroup{}
118 | wgCheck.Add(len(allDataModel))
119 | for _, m := range allDataModel {
120 | go func(m *model.TableModel) {
121 | check.Run(m, global)
122 | wgCheck.Done()
123 | }(m)
124 | }
125 | wgCheck.Wait()
126 |
127 | tableMap := make(map[string]*model.TableModel, len(allDataModel))
128 | for _, m := range allDataModel {
129 | tableMap[m.Meta.Target] = m
130 | }
131 |
132 | // 全局单次的后处理
133 | postScriptPath := strings.TrimSpace(rulePlus.GetPostGlobalScriptPath())
134 | if postScriptPath != "" {
135 | fileExt := filepath.Ext(postScriptPath)
136 | switch fileExt {
137 | case ".js":
138 | extPostGlobal, err := ext_post.NewExtPostGlobalJS(postScriptPath)
139 | if err != nil {
140 | slog.Fatalf("ExtPostGlobal Script Files laod error filePath:%s err:%v", postScriptPath, err)
141 | os.Exit(1)
142 | }
143 | err = extPostGlobal.PostGlobal(tableMap)
144 | if err != nil {
145 | slog.Fatalf("ExtPostGlobal Script post process error filePath:%s err:%v", postScriptPath, err)
146 | os.Exit(1)
147 | }
148 | default:
149 | slog.Fatalf("ext post global script type not support:%s", fileExt)
150 | }
151 | }
152 |
153 | return allDataModel
154 | }
155 |
--------------------------------------------------------------------------------
/data/memory_table/memory_table_common.go:
--------------------------------------------------------------------------------
1 | package memory_table
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/821869798/table-export/config"
7 | "github.com/821869798/table-export/convert/wrap"
8 | "github.com/821869798/table-export/data/apidata"
9 | "github.com/821869798/table-export/data/model"
10 | "github.com/821869798/table-export/meta"
11 | "github.com/gookit/slog"
12 | )
13 |
14 | // MemoryTableCommon Table中所有的key的类型有不一样的,只能使用interface{}
15 | type MemoryTableCommon struct {
16 | ValueMapping map[interface{}]interface{}
17 | ValueList []map[string]interface{}
18 | RowDataIndex []int // 数据所在的原始行,可能之后会支持删除某一行
19 | ExtraData map[string]interface{} // 额外的数据,一般是后处理添加的
20 | Name string
21 | tableMeta *meta.TableMeta
22 | }
23 |
24 | func NewMemTableCommon(dataModel *model.TableModel, count, cap int) (apidata.IMemoryTable, error) {
25 | // count是有效数据,cap是容量,兼容以后可能允许空行的情况
26 | memTable := &MemoryTableCommon{
27 | ValueMapping: make(map[interface{}]interface{}, count),
28 | ValueList: make([]map[string]interface{}, 0, count),
29 | RowDataIndex: make([]int, cap),
30 | ExtraData: make(map[string]interface{}),
31 | Name: dataModel.Meta.Target,
32 | tableMeta: dataModel.Meta,
33 | }
34 | err := memTable.ReadTableModel(dataModel)
35 | if err != nil {
36 | return nil, err
37 | }
38 | return memTable, nil
39 | }
40 |
41 | func (m *MemoryTableCommon) TableName() string {
42 | return m.Name
43 | }
44 |
45 | func (m *MemoryTableCommon) RawDataMapping() map[interface{}]interface{} {
46 | return m.ValueMapping
47 | }
48 |
49 | func (m *MemoryTableCommon) RawDataList() []map[string]interface{} {
50 | return m.ValueList
51 | }
52 |
53 | func (m *MemoryTableCommon) ReadTableModel(dataModel *model.TableModel) error {
54 | rowDataOffset := config.GlobalConfig.Table.DataStart + 1
55 | for rowIndex, rowData := range dataModel.RawData {
56 | //数据表中一行数据的字符串
57 | recordMap := make(map[string]interface{}, len(dataModel.Meta.Fields))
58 | keys := make([]interface{}, len(dataModel.Meta.Keys))
59 | for _, tf := range dataModel.Meta.Fields {
60 | rawIndex := dataModel.NameIndexMapping[tf.Target]
61 | var rawStr string
62 | if rawIndex < len(rowData) {
63 | rawStr = rowData[rawIndex]
64 | }
65 | output, err := wrap.GetOutputValue(config.ExportType_Json, tf.Type, rawStr)
66 | if err != nil {
67 | return errors.New(fmt.Sprintf("create memory table failed,file[%v] RowCount[%v] filedName[%v] error:%v", dataModel.Meta.Target, rowIndex+rowDataOffset, tf.Source, err))
68 | }
69 |
70 | recordMap[tf.Target] = output
71 |
72 | //存储key
73 | if tf.Key > 0 {
74 | keys[tf.Key-1] = output
75 | }
76 | }
77 |
78 | //写入key和数据
79 | dataMap := m.ValueMapping
80 | for i := 0; i < len(keys)-1; i++ {
81 | keyStr := keys[i]
82 | tmpInterface, ok := dataMap[keyStr]
83 | var tmpMap map[interface{}]interface{}
84 | if !ok {
85 | tmpMap = make(map[interface{}]interface{})
86 | dataMap[keyStr] = tmpMap
87 | } else {
88 | tmpMap = tmpInterface.(map[interface{}]interface{})
89 | }
90 | dataMap = tmpMap
91 | }
92 |
93 | //判断唯一key是否重复
94 | lastKey := keys[len(keys)-1]
95 | _, ok := dataMap[lastKey]
96 | if ok {
97 | return errors.New(fmt.Sprintf("create memory table failed,file[%v] RowCount[%v] key is repeated:%v", dataModel.Meta.Target, rowIndex+rowDataOffset, keys))
98 | }
99 |
100 | dataMap[lastKey] = recordMap
101 | m.ValueList = append(m.ValueList, recordMap)
102 |
103 | // 这里没有-1是为了避免为0,把0当作无效数据,不然没法区分
104 | m.RowDataIndex[rowIndex] = len(m.ValueList)
105 | }
106 |
107 | return nil
108 | }
109 |
110 | func (m *MemoryTableCommon) GetRecordByRow(rowIndex int) (map[string]interface{}, error) {
111 | recordIndex := m.RowDataIndex[rowIndex]
112 | if recordIndex > 0 {
113 | return m.ValueList[recordIndex-1], nil
114 | }
115 | return nil, errors.New(fmt.Sprintf("rowIndex[%v] is not exist", rowIndex))
116 | }
117 |
118 | func (m *MemoryTableCommon) GetRecordRecordMap(recordIndex int) map[string]interface{} {
119 | return m.ValueList[recordIndex-1]
120 | }
121 |
122 | func (m *MemoryTableCommon) GetRecordByKey(keys ...interface{}) map[string]interface{} {
123 | if len(keys) != len(m.tableMeta.Keys) {
124 | return nil
125 | }
126 | dataMap := m.ValueMapping
127 | for i := 0; i < len(keys)-1; i++ {
128 | value, err := wrap.GetFormatValueInterface(m.tableMeta.Keys[i].Type, keys[i])
129 | if err != nil {
130 | slog.Fatalf("get format value interface error:%v", err)
131 | }
132 | tmpInterface, ok := dataMap[value]
133 | var tmpMap map[interface{}]interface{}
134 | if !ok {
135 | return nil
136 | } else {
137 | tmpMap = tmpInterface.(map[interface{}]interface{})
138 | }
139 | dataMap = tmpMap
140 | }
141 | lastKey := keys[len(keys)-1]
142 | value, err := wrap.GetFormatValueInterface(m.tableMeta.Keys[len(keys)-1].Type, lastKey)
143 | if err != nil {
144 | slog.Fatalf("get format value interface error:%v", err)
145 | }
146 | return dataMap[value].(map[string]interface{})
147 | }
148 |
149 | func (m *MemoryTableCommon) RowIndexList() []int {
150 | return m.RowDataIndex
151 | }
152 |
153 | func (m *MemoryTableCommon) AddExtraData(name string, data interface{}) {
154 | m.ExtraData[name] = data
155 | }
156 |
157 | func (m *MemoryTableCommon) RemoveExtraData(name string) {
158 | delete(m.ExtraData, name)
159 | }
160 |
161 | func (m *MemoryTableCommon) GetExtraDataMap() map[string]interface{} {
162 | return m.ExtraData
163 | }
164 |
--------------------------------------------------------------------------------
/examples/projects/proj_cs_bin/src/gen/Tblcomplex_test.cs:
--------------------------------------------------------------------------------
1 | using Serialization;
2 |
3 | using System.Collections.Generic;
4 |
5 | namespace CfgTable
6 | {
7 |
8 | public partial class Tblcomplex_test
9 | {
10 |
11 | private readonly Dictionary> _dataMap;
12 | private readonly List _dataList;
13 |
14 |
15 | public Tblcomplex_test(ByteBuf _buf)
16 | {
17 | //first read common data
18 | _TbCommoncomplex_test _commonData = null;
19 | var commonSize = _buf.ReadSize();
20 | if( commonSize > 0)
21 | {
22 | _commonData = new _TbCommoncomplex_test(_buf);
23 | }
24 |
25 | var size = _buf.ReadSize();
26 | _dataMap = new Dictionary>(16);
27 | _dataList = new List(size);
28 |
29 | for (int i = 0; i < size; i++)
30 | {
31 | Cfgcomplex_test _v;
32 | _v = Cfgcomplex_test.DeserializeCfgcomplex_test(_buf, _commonData);
33 | _dataList.Add(_v);
34 | if (!_dataMap.TryGetValue(_v.key1, out var _tmpuk0))
35 | {
36 | _tmpuk0 = new Dictionary();
37 | _dataMap[_v.key1] = _tmpuk0;
38 | }
39 | _tmpuk0[_v.key2] = _v;
40 | }
41 |
42 |
43 | PostInit();
44 | }
45 |
46 | public int DataCount => _dataList.Count;
47 | public Dictionary> DataMap => _dataMap;
48 | public List DataList => _dataList;
49 |
50 | public Cfgcomplex_test Get(int __k0, string __k1)
51 | {
52 | if (_dataMap.TryGetValue(__k0, out var __tmpv0) && __tmpv0.TryGetValue(__k1, out var __tmpv1)) { return __tmpv1; }
53 | #if UNITY_EDITOR
54 | Debug.LogError($"[Tblcomplex_test] config id not found,id:{__k0.ToString() + " " + __k1.ToString()}");
55 | #endif
56 | return null;
57 | }
58 |
59 | public Cfgcomplex_test GetWithoutError(int __k0, string __k1)
60 | {
61 | if (_dataMap.TryGetValue(__k0, out var __tmpv0) && __tmpv0.TryGetValue(__k1, out var __tmpv1)) { return __tmpv1; }
62 | return null;
63 | }
64 |
65 |
66 | ///
67 | /// table data file
68 | ///
69 | public static string TableFileName { get; } = "complex_test";
70 |
71 | ///
72 | /// post process table
73 | ///
74 | partial void PostInit();
75 |
76 | }
77 |
78 | public partial class Cfgcomplex_test
79 | {
80 | private Cfgcomplex_test(ByteBuf _buf, _TbCommoncomplex_test _commonData)
81 | {
82 |
83 | key1 = _buf.ReadInt();
84 | key2 = _buf.ReadString();
85 | { int dataIndex = _buf.ReadInt() - 1; content = _commonData._field0[dataIndex]; }
86 | number = _buf.ReadFloat();
87 | { int dataIndex = _buf.ReadInt() - 1; test_list = _commonData._field1[dataIndex]; }
88 | { int dataIndex = _buf.ReadInt() - 1; test_map = _commonData._field2[dataIndex]; }
89 | PostInit();
90 | }
91 |
92 | internal static Cfgcomplex_test DeserializeCfgcomplex_test(ByteBuf _buf, _TbCommoncomplex_test _commonData)
93 | {
94 | return new Cfgcomplex_test(_buf, _commonData);
95 | }
96 |
97 | ///
98 | /// id
99 | ///
100 | public int key1 { get; private set; }
101 |
102 | ///
103 | /// id2
104 | ///
105 | public string key2 { get; private set; }
106 |
107 | ///
108 | /// 内容
109 | ///
110 | public string content { get; private set; }
111 |
112 | ///
113 | /// 数字
114 | ///
115 | public float number { get; private set; }
116 |
117 | ///
118 | /// 列表
119 | ///
120 | public int[] test_list { get; private set; }
121 |
122 | ///
123 | /// 字典
124 | ///
125 | public Dictionary test_map { get; private set; }
126 |
127 |
128 | ///
129 | /// post process table
130 | ///
131 | partial void PostInit();
132 | }
133 |
134 | ///
135 | /// internal common data optimize
136 | ///
137 | internal class _TbCommoncomplex_test
138 | {
139 |
140 | internal string[] _field0 { get; private set; }
141 | internal int[][] _field1 { get; private set; }
142 | internal Dictionary[] _field2 { get; private set; }
143 | internal _TbCommoncomplex_test(ByteBuf _buf)
144 | {
145 |
146 | {int __n0 = _buf.ReadSize(); _field0 = new string[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ string __v0; __v0 = _buf.ReadString(); _field0[__i0] = __v0; } }
147 | {int __n0 = _buf.ReadSize(); _field1 = new int[__n0][]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ int[] __v0; {int __n1 = _buf.ReadSize(); __v0 = new int[__n1]; for(var __i1 = 0 ; __i1 < __n1 ; __i1++ ){ int __v1; __v1 = _buf.ReadInt(); __v0[__i1] = __v1; } } _field1[__i0] = __v0; } }
148 | {int __n0 = _buf.ReadSize(); _field2 = new Dictionary[__n0]; for(var __i0 = 0 ; __i0 < __n0 ; __i0++ ){ Dictionary __v0; { int __n1 = _buf.ReadSize(); __v0 = new Dictionary (__n1 * 3 / 2); for(var __i1 = 0 ; __i1 < __n1 ; __i1++ ) {int __k1; __k1 = _buf.ReadInt(); string __v1; __v1 = _buf.ReadString(); __v0.Add(__k1, __v1); } } _field2[__i0] = __v0; } }
149 | }
150 |
151 | }
152 |
153 | }
154 |
--------------------------------------------------------------------------------