├── 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 | <d 5 -------------------------------------------------------------------------------- /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 | ![before_optimization](doc/img/before_optimization.png) 118 | 119 | **开启优化后** 120 | 121 | ![after_optimization](doc/img/after_optimization.png) 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 | --------------------------------------------------------------------------------