diff --git a/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go b/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go index e136cc36..88d98056 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go +++ b/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go @@ -165,50 +165,58 @@ func ParseCppClassType(md protoreflect.MessageDescriptor) string { return strings.ReplaceAll(protoFullName, ".", "::") } +func ParseLeveledMapPrefix(md protoreflect.MessageDescriptor, mapFd protoreflect.FieldDescriptor) string { + if mapFd.MapValue().Kind() == protoreflect.MessageKind { + localMsgProtoName := strings.TrimPrefix(string(mapFd.MapValue().Message().FullName()), string(md.FullName())+".") + return strings.ReplaceAll(localMsgProtoName, ".", "_") + } + return mapFd.MapValue().Kind().String() +} + type MapKey struct { Type string Name string } -type MapKeys []MapKey +type MapKeySlice []MapKey -func (keys MapKeys) AddMapKey(newKey MapKey) MapKeys { +func (s MapKeySlice) AddMapKey(newKey MapKey) MapKeySlice { if newKey.Name == "" { - newKey.Name = fmt.Sprintf("key%d", len(keys)+1) + newKey.Name = fmt.Sprintf("key%d", len(s)+1) } else { - for _, key := range keys { + for _, key := range s { if key.Name == newKey.Name { // rewrite to avoid name confict - newKey.Name = fmt.Sprintf("%s%d", newKey.Name, len(keys)+1) + newKey.Name = fmt.Sprintf("%s%d", newKey.Name, len(s)+1) break } } } - return append(keys, newKey) + return append(s, newKey) } // GenGetParams generates function parameters, which are the names listed in the function's definition. -func (keys MapKeys) GenGetParams() string { +func (s MapKeySlice) GenGetParams() string { var params []string - for _, key := range keys { + for _, key := range s { params = append(params, ToConstRefType(key.Type)+" "+key.Name) } return strings.Join(params, ", ") } // GenGetArguments generates function arguments, which are the real values passed to the function. -func (keys MapKeys) GenGetArguments() string { +func (s MapKeySlice) GenGetArguments() string { var params []string - for _, key := range keys { + for _, key := range s { params = append(params, key.Name) } return strings.Join(params, ", ") } // GenOtherArguments generates function arguments for other value of std::tie. -func (keys MapKeys) GenOtherArguments(other string) string { +func (s MapKeySlice) GenOtherArguments(other string) string { var params []string - for _, key := range keys { + for _, key := range s { params = append(params, other+"."+key.Name) } return strings.Join(params, ", ") @@ -218,6 +226,10 @@ func Indent(depth int) string { return strings.Repeat(" ", depth) } +func Whitespace(count int) string { + return strings.Repeat(" ", count) +} + func ParseMapValueType(fd protoreflect.FieldDescriptor) string { valueType := ParseCppType(fd.MapValue()) if fd.MapValue().Kind() == protoreflect.MessageKind { diff --git a/cmd/protoc-gen-cpp-tableau-loader/index/index.go b/cmd/protoc-gen-cpp-tableau-loader/index/index.go deleted file mode 100644 index 14f4562a..00000000 --- a/cmd/protoc-gen-cpp-tableau-loader/index/index.go +++ /dev/null @@ -1,309 +0,0 @@ -package index - -import ( - "fmt" - "strings" - - "github.com/iancoleman/strcase" - "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" - "github.com/tableauio/loader/internal/index" - "github.com/tableauio/loader/internal/options" - "google.golang.org/protobuf/compiler/protogen" - "google.golang.org/protobuf/reflect/protoreflect" -) - -type Generator struct { - g *protogen.GeneratedFile - descriptor *index.IndexDescriptor - message *protogen.Message -} - -func NewGenerator(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { - return &Generator{ - g: g, - descriptor: descriptor, - message: message, - } -} - -func (x *Generator) NeedGenerate() bool { - return options.NeedGenIndex(x.message.Desc, options.LangCPP) -} - -func (x *Generator) messagerName() string { - return string(x.message.Desc.Name()) -} - -func (x *Generator) mapType(index *index.LevelIndex) string { - return fmt.Sprintf("Index_%sMap", index.Name()) -} - -func (x *Generator) mapKeyType(index *index.LevelIndex) string { - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take first field - return helper.ParseCppType(field.FD) - } else { - // multi-column index - return fmt.Sprintf("Index_%sKey", index.Name()) - } -} - -func (x *Generator) mapValueType(index *index.LevelIndex) string { - return helper.ParseCppClassType(index.MD) -} - -func (x *Generator) mapValueVectorType(index *index.LevelIndex) string { - return fmt.Sprintf("Index_%sVector", index.Name()) -} - -func (x *Generator) indexContainerName(index *index.LevelIndex) string { - return fmt.Sprintf("index_%s_map_", strcase.ToSnake(index.Name())) -} - -func (x *Generator) indexKeys(index *index.LevelIndex) helper.MapKeys { - var keys helper.MapKeys - for _, field := range index.ColFields { - keys = keys.AddMapKey(helper.MapKey{ - Type: helper.ParseCppType(field.FD), - Name: helper.ParseIndexFieldNameAsFuncParam(field.FD), - }) - } - return keys -} - -func (x *Generator) fieldGetter(fd protoreflect.FieldDescriptor) string { - return fmt.Sprintf(".%s()", helper.ParseIndexFieldName(fd)) -} - -func (x *Generator) parseKeyFieldName(field *index.LevelField) string { - var fieldName string - for _, leveledFd := range field.LeveledFDList { - fieldName += x.fieldGetter(leveledFd) - } - return fieldName -} - -func (x *Generator) GenHppIndexFinders() { - if !x.NeedGenerate() { - return - } - x.g.P() - x.g.P(helper.Indent(1), "// Index accessers.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - x.g.P(helper.Indent(1), "// Index: ", index.Index) - x.g.P(" public:") - mapType := x.mapType(index) - keyType := x.mapKeyType(index) - vectorType := x.mapValueVectorType(index) - valueType := x.mapValueType(index) - keys := x.indexKeys(index) - hasher := "" // std::hash by default - if len(index.ColFields) != 1 { - // multi-column index - keyHasherType := fmt.Sprintf("Index_%sKeyHasher", index.Name()) - hasher = ", " + keyHasherType - // Generate key struct - x.g.P(helper.Indent(1), "struct ", keyType, " {") - for _, key := range keys { - x.g.P(helper.Indent(2), key.Type, " ", key.Name, ";") - } - x.g.P("#if __cplusplus >= 202002L") - x.g.P(helper.Indent(2), "bool operator==(const ", keyType, "& other) const = default;") - x.g.P("#else") - x.g.P(helper.Indent(2), "bool operator==(const ", keyType, "& other) const {") - if len(keys) == 1 { - x.g.P(helper.Indent(3), "return ", keys[0].Name, " == other.", keys[0].Name, ";") - } else { - x.g.P(helper.Indent(3), "return std::tie(", keys.GenGetArguments(), ") == std::tie(", keys.GenOtherArguments("other"), ");") - } - // x.g.P(helper.Indent(3), "return ", strings.Join(equalities, " && "), ";") - x.g.P(helper.Indent(2), "}") - x.g.P("#endif") - x.g.P(helper.Indent(1), "};") - - // Generate key hasher struct - x.g.P(helper.Indent(1), "struct ", keyHasherType, " {") - x.g.P(helper.Indent(2), "std::size_t operator()(const ", keyType, "& key) const {") - x.g.P(helper.Indent(3), "return util::SugaredHashCombine(", keys.GenOtherArguments("key"), ");") - x.g.P(helper.Indent(2), "}") - x.g.P(helper.Indent(1), "};") - } - x.g.P(helper.Indent(1), "using ", vectorType, " = std::vector;") - x.g.P(helper.Indent(1), "using ", mapType, " = std::unordered_map<", keyType, ", ", vectorType, hasher, ">;") - x.g.P(helper.Indent(1), "// Finds the index (", index.Index, ") to value (", vectorType, ") hash map.") - x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are contained by a vector.") - x.g.P(helper.Indent(1), "const ", mapType, "& Find", index.Name(), "() const;") - x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s).") - x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") - x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") - x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") - x.g.P() - x.g.P(" private:") - x.g.P(helper.Indent(1), mapType, " ", x.indexContainerName(index), ";") - x.g.P() - } - } -} - -func (x *Generator) GenCppIndexLoader() { - if !x.NeedGenerate() { - return - } - x.g.P(helper.Indent(1), "// Index init.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - x.g.P(helper.Indent(1), x.indexContainerName(index), ".clear();") - } - } - parentDataName := "data_" - depth := 1 - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - x.genOneCppIndexLoader(depth, index, parentDataName) - } - itemName := fmt.Sprintf("item%d", depth) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGen() { - break - } - x.g.P(helper.Indent(depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") - parentDataName = itemName - if levelMessage.FD.IsMap() { - parentDataName = itemName + ".second" - } - depth++ - } - for i := depth - 1; i > 0; i-- { - x.g.P(helper.Indent(i), "}") - } - x.genIndexSorter() -} - -func (x *Generator) genOneCppIndexLoader(depth int, index *index.LevelIndex, parentDataName string) { - x.g.P(helper.Indent(depth), "{") - x.g.P(helper.Indent(depth+1), "// Index: ", index.Index) - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take the first field - fieldName := x.parseKeyFieldName(field) - indexContainerName := x.indexContainerName(index) - if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) - x.g.P(helper.Indent(depth+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") - key := itemName - if field.FD.Enum() != nil { - key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" - } - x.g.P(helper.Indent(depth+2), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") - x.g.P(helper.Indent(depth+1), "}") - } else { - key := parentDataName + fieldName - x.g.P(helper.Indent(depth+1), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") - } - } else { - // multi-column index - x.generateOneCppMulticolumnIndex(depth, index, parentDataName, nil) - } - x.g.P(helper.Indent(depth), "}") -} - -func (x *Generator) generateOneCppMulticolumnIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeys) { - cursor := len(keys) - if cursor >= len(index.ColFields) { - keyType := x.mapKeyType(index) - indexContainerName := x.indexContainerName(index) - x.g.P(helper.Indent(depth+1), keyType, " key{", keys.GenGetArguments(), "};") - x.g.P(helper.Indent(depth+1), indexContainerName, "[key].push_back(&", parentDataName, ");") - return - } - field := index.ColFields[cursor] - fieldName := x.parseKeyFieldName(field) - if field.FD.IsList() { - itemName := fmt.Sprintf("index_item%d", cursor) - x.g.P(helper.Indent(depth+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") - key := itemName - if field.FD.Enum() != nil { - key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" - } - keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnIndex(depth+1, index, parentDataName, keys) - x.g.P(helper.Indent(depth+1), "}") - } else { - key := parentDataName + fieldName - keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnIndex(depth, index, parentDataName, keys) - } -} - -func (x *Generator) genIndexSorter() { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - if len(index.SortedColFields) != 0 { - valueType := x.mapValueType(index) - x.g.P(helper.Indent(1), "// Index(sort): ", index.Index) - x.g.P(helper.Indent(1), "for (auto&& item : ", x.indexContainerName(index), ") {") - x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(),") - x.g.P(helper.Indent(7), "[](const ", valueType, "* a, const ", valueType, "* b) {") - for i, field := range index.SortedColFields { - fieldName := strings.Replace(x.parseKeyFieldName(field), ".", "->", 1) - if i == len(index.SortedColFields)-1 { - x.g.P(helper.Indent(8), "return a", fieldName, " < b", fieldName, ";") - } else { - x.g.P(helper.Indent(8), "if (a", fieldName, " != b", fieldName, ") {") - x.g.P(helper.Indent(9), "return a", fieldName, " < b", fieldName, ";") - x.g.P(helper.Indent(8), "}") - } - } - x.g.P(helper.Indent(7), "});") - x.g.P(helper.Indent(1), "}") - } - } - } -} - -func (x *Generator) GenCppIndexFinders() { - if !x.NeedGenerate() { - return - } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - vectorType := x.mapValueVectorType(index) - mapType := x.mapType(index) - indexContainerName := x.indexContainerName(index) - messagerName := x.messagerName() - - x.g.P("// Index: ", index.Index) - x.g.P("const ", messagerName, "::", mapType, "& ", messagerName, "::Find", index.Name(), "() const { return ", indexContainerName, " ;}") - x.g.P() - - keys := x.indexKeys(index) - params := keys.GenGetParams() - args := keys.GenGetArguments() - x.g.P("const ", messagerName, "::", vectorType, "* ", messagerName, "::Find", index.Name(), "(", params, ") const {") - if len(index.ColFields) == 1 { - x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find(", args, ");") - } else { - x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find({", args, "});") - } - x.g.P(helper.Indent(1), "if (iter == ", indexContainerName, ".end()) {") - x.g.P(helper.Indent(2), "return nullptr;") - x.g.P(helper.Indent(1), "}") - x.g.P(helper.Indent(1), "return &iter->second;") - x.g.P("}") - x.g.P() - - x.g.P("const ", x.mapValueType(index), "* ", messagerName, "::FindFirst", index.Name(), "(", params, ") const {") - x.g.P(helper.Indent(1), "auto conf = Find", index.Name(), "(", args, ");") - x.g.P(helper.Indent(1), "if (conf == nullptr || conf->empty()) {") - x.g.P(helper.Indent(2), "return nullptr;") - x.g.P(helper.Indent(1), "}") - x.g.P(helper.Indent(1), "return conf->front();") - x.g.P("}") - x.g.P() - } - } -} diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go new file mode 100644 index 00000000..b6f821fe --- /dev/null +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -0,0 +1,126 @@ +package indexes + +import ( + "fmt" + + "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" + "github.com/tableauio/loader/internal/index" + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type Generator struct { + g *protogen.GeneratedFile + descriptor *index.IndexDescriptor + message *protogen.Message + + // level message + maxDepth int + keys helper.MapKeySlice + mapFds []protoreflect.FieldDescriptor +} + +func NewGenerator(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { + generator := &Generator{ + g: g, + descriptor: descriptor, + message: message, + } + generator.initLevelMessage() + return generator +} + +func (x *Generator) initLevelMessage() { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + if fd := levelMessage.FD; fd != nil && fd.IsMap() { + x.keys = x.keys.AddMapKey(helper.MapKey{ + Type: helper.ParseMapKeyType(fd.MapKey()), + Name: helper.ParseMapFieldName(fd), + }) + x.mapFds = append(x.mapFds, fd) + } + if len(levelMessage.Indexes) != 0 || len(levelMessage.OrderedIndexes) != 0 { + x.maxDepth = levelMessage.MapDepth + } + } +} + +func (x *Generator) NeedGenerate() bool { + return x.needGenerateIndex() || x.needGenerateOrderedIndex() +} + +func (x *Generator) messagerName() string { + return string(x.message.Desc.Name()) +} + +func (x *Generator) levelKeyType(mapFd protoreflect.FieldDescriptor) string { + return fmt.Sprintf("LevelIndex_%sKey", helper.ParseLeveledMapPrefix(x.message.Desc, mapFd)) +} + +func (x *Generator) mapValueType(index *index.LevelIndex) string { + return helper.ParseCppClassType(index.MD) +} + +func (x *Generator) fieldGetter(fd protoreflect.FieldDescriptor) string { + return fmt.Sprintf(".%s()", helper.ParseIndexFieldName(fd)) +} + +func (x *Generator) parseKeyFieldNameAndSuffix(field *index.LevelField) (string, string) { + var fieldName, suffix string + for i, leveledFd := range field.LeveledFDList { + fieldName += x.fieldGetter(leveledFd) + if i == len(field.LeveledFDList)-1 && leveledFd.Message() != nil { + switch leveledFd.Message().FullName() { + case "google.protobuf.Timestamp", "google.protobuf.Duration": + suffix = ".seconds()" + default: + } + } + } + return fieldName, suffix +} + +func (x *Generator) GenHppIndexFinders() { + if !x.NeedGenerate() { + return + } + for i := 1; i <= x.maxDepth-3 && i <= len(x.mapFds)-1; i++ { + if i == 1 { + x.g.P() + x.g.P(helper.Indent(1), "// LevelIndex keys.") + x.g.P(" public:") + } + fd := x.mapFds[i] + keyType := x.levelKeyType(fd) + x.g.P(helper.Indent(1), "struct ", keyType, " {") + keys := x.keys[:i+1] + for _, key := range keys { + x.g.P(helper.Indent(2), key.Type, " ", key.Name, ";") + } + x.g.P("#if __cplusplus >= 202002L") + x.g.P(helper.Indent(2), "bool operator==(const ", keyType, "& other) const = default;") + x.g.P("#else") + x.g.P(helper.Indent(2), "bool operator==(const ", keyType, "& other) const {") + x.g.P(helper.Indent(3), "return std::tie(", keys.GenGetArguments(), ") == std::tie(", keys.GenOtherArguments("other"), ");") + x.g.P(helper.Indent(2), "}") + x.g.P("#endif") + x.g.P(helper.Indent(1), "};") + x.g.P(helper.Indent(1), "struct ", keyType, "Hasher {") + x.g.P(helper.Indent(2), "std::size_t operator()(const ", keyType, "& key) const {") + x.g.P(helper.Indent(3), "return util::SugaredHashCombine(", keys.GenOtherArguments("key"), ");") + x.g.P(helper.Indent(2), "}") + x.g.P(helper.Indent(1), "};") + } + x.genHppIndexFinders() + x.genHppOrderedIndexFinders() +} + +func (x *Generator) GenIndexLoader() { + x.genIndexLoader() + x.genOrderedIndexLoader() +} + +func (x *Generator) GenCppIndexFinders() { + x.genCppIndexFinders() + x.genCppOrderedIndexFinders() +} diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go new file mode 100644 index 00000000..689f1ab8 --- /dev/null +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go @@ -0,0 +1,380 @@ +package indexes + +import ( + "fmt" + "strings" + "sync" + + "github.com/iancoleman/strcase" + "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" + "github.com/tableauio/loader/internal/index" + "github.com/tableauio/loader/internal/loadutil" + "github.com/tableauio/loader/internal/options" +) + +func (x *Generator) needGenerateIndex() bool { + return options.NeedGenIndex(x.message.Desc, options.LangCPP) +} + +func (x *Generator) indexMapType(index *index.LevelIndex) string { + return fmt.Sprintf("Index_%sMap", index.Name()) +} + +func (x *Generator) indexMapKeyType(index *index.LevelIndex) string { + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + return helper.ParseCppType(field.FD) + } else { + // multi-column index + return fmt.Sprintf("Index_%sKey", index.Name()) + } +} + +func (x *Generator) indexMapValueVectorType(index *index.LevelIndex) string { + return fmt.Sprintf("Index_%sVector", index.Name()) +} + +func (x *Generator) indexContainerName(index *index.LevelIndex, i int) string { + if i == 0 { + return fmt.Sprintf("index_%s_map_", strcase.ToSnake(index.Name())) + } + return fmt.Sprintf("index_%s_map%d_", strcase.ToSnake(index.Name()), i) +} + +func (x *Generator) indexKeys(index *index.LevelIndex) helper.MapKeySlice { + var keys helper.MapKeySlice + for _, field := range index.ColFields { + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseCppType(field.FD), + Name: helper.ParseIndexFieldNameAsFuncParam(field.FD), + }) + } + return keys +} + +func (x *Generator) genHppIndexFinders() { + if !x.needGenerateIndex() { + return + } + var once sync.Once + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.g.P() + once.Do(func() { x.g.P(helper.Indent(1), "// Index accessers.") }) + x.g.P(helper.Indent(1), "// Index: ", index.Index) + x.g.P(" public:") + mapType := x.indexMapType(index) + keyType := x.indexMapKeyType(index) + vectorType := x.indexMapValueVectorType(index) + valueType := x.mapValueType(index) + keys := x.indexKeys(index) + hasher := "" // std::hash by default + if len(index.ColFields) != 1 { + // multi-column index + keyHasherType := fmt.Sprintf("Index_%sKeyHasher", index.Name()) + hasher = ", " + keyHasherType + // Generate key struct + x.g.P(helper.Indent(1), "struct ", keyType, " {") + for _, key := range keys { + x.g.P(helper.Indent(2), key.Type, " ", key.Name, ";") + } + x.g.P("#if __cplusplus >= 202002L") + x.g.P(helper.Indent(2), "bool operator==(const ", keyType, "& other) const = default;") + x.g.P("#else") + x.g.P(helper.Indent(2), "bool operator==(const ", keyType, "& other) const {") + x.g.P(helper.Indent(3), "return std::tie(", keys.GenGetArguments(), ") == std::tie(", keys.GenOtherArguments("other"), ");") + x.g.P(helper.Indent(2), "}") + x.g.P("#endif") + x.g.P(helper.Indent(1), "};") + + // Generate key hasher struct + x.g.P(helper.Indent(1), "struct ", keyHasherType, " {") + x.g.P(helper.Indent(2), "std::size_t operator()(const ", keyType, "& key) const {") + x.g.P(helper.Indent(3), "return util::SugaredHashCombine(", keys.GenOtherArguments("key"), ");") + x.g.P(helper.Indent(2), "}") + x.g.P(helper.Indent(1), "};") + } + x.g.P(helper.Indent(1), "using ", vectorType, " = std::vector;") + x.g.P(helper.Indent(1), "using ", mapType, " = std::unordered_map<", keyType, ", ", vectorType, hasher, ">;") + x.g.P(helper.Indent(1), "// Finds the index: key(", index.Index, ") to value(", vectorType, ") hashmap.") + x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are represented by a vector.") + x.g.P(helper.Indent(1), "const ", mapType, "& Find", index.Name(), "Map() const;") + x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s).") + x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") + x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") + x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + partKeys := x.keys[:i] + x.g.P(helper.Indent(1), "// Finds the index: key(", index.Index, ") to value(", vectorType, "),") + x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are represented by a vector.") + x.g.P(helper.Indent(1), "const ", mapType, "* Find", index.Name(), "Map(", partKeys.GenGetParams(), ") const;") + x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") + x.g.P(helper.Indent(1), "// Finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") + } + x.g.P() + + x.g.P(" private:") + x.g.P(helper.Indent(1), mapType, " ", x.indexContainerName(index, 0), ";") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + if i == 1 { + x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.indexContainerName(index, i), ";") + } else { + levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.indexContainerName(index, i), ";") + } + } + } + } +} + +func (x *Generator) genIndexLoader() { + if !x.needGenerateIndex() { + return + } + defer x.genIndexSorter() + x.g.P(helper.Indent(1), "// Index init.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.g.P(helper.Indent(1), x.indexContainerName(index, 0), ".clear();") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + x.g.P(helper.Indent(1), x.indexContainerName(index, i), ".clear();") + } + } + } + parentDataName := "data_" + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.genOneCppIndexLoader(levelMessage.MapDepth, levelMessage.Depth, index, parentDataName) + } + itemName := fmt.Sprintf("item%d", levelMessage.Depth) + if levelMessage.FD == nil { + break + } + if !levelMessage.NextLevel.NeedGenIndex() { + break + } + x.g.P(helper.Indent(levelMessage.Depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") + parentDataName = itemName + if levelMessage.FD.IsMap() { + x.g.P(helper.Indent(levelMessage.Depth+1), "auto k", levelMessage.MapDepth, " = ", itemName, ".first;") + parentDataName = itemName + ".second" + } + defer x.g.P(helper.Indent(levelMessage.Depth), "}") + } +} + +func (x *Generator) genOneCppIndexLoader(depth int, ident int, index *index.LevelIndex, parentDataName string) { + x.g.P(helper.Indent(ident), "{") + x.g.P(helper.Indent(ident+1), "// Index: ", index.Index) + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take the first field + fieldName, _ := x.parseKeyFieldNameAndSuffix(field) + if field.FD.IsList() { + itemName := fmt.Sprintf("item%d", depth) + x.g.P(helper.Indent(ident+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") + key := itemName + if field.FD.Enum() != nil { + key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" + } + x.genLoader(depth, ident+2, index, key, parentDataName) + x.g.P(helper.Indent(ident+1), "}") + } else { + key := parentDataName + fieldName + x.genLoader(depth, ident+1, index, key, parentDataName) + } + } else { + // multi-column index + x.generateOneCppMulticolumnIndex(depth, ident, index, parentDataName, nil) + } + x.g.P(helper.Indent(ident), "}") +} + +func (x *Generator) generateOneCppMulticolumnIndex(depth, ident int, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { + cursor := len(keys) + if cursor >= len(index.ColFields) { + keyType := x.indexMapKeyType(index) + x.g.P(helper.Indent(ident+1), keyType, " key{", keys.GenGetArguments(), "};") + x.genLoader(depth, ident+1, index, "key", parentDataName) + return + } + field := index.ColFields[cursor] + fieldName, _ := x.parseKeyFieldNameAndSuffix(field) + if field.FD.IsList() { + itemName := fmt.Sprintf("index_item%d", cursor) + x.g.P(helper.Indent(ident+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") + key := itemName + if field.FD.Enum() != nil { + key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" + } + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneCppMulticolumnIndex(depth, ident+1, index, parentDataName, keys) + x.g.P(helper.Indent(ident+1), "}") + } else { + key := parentDataName + fieldName + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneCppMulticolumnIndex(depth, ident, index, parentDataName, keys) + } +} + +func (x *Generator) genLoader(depth, ident int, index *index.LevelIndex, key, parentDataName string) { + x.g.P(helper.Indent(ident), x.indexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") + for i := 1; i <= depth-2; i++ { + if i > len(x.keys) { + break + } + if i == 1 { + x.g.P(helper.Indent(ident), x.indexContainerName(index, i), "[k1][", key, "].push_back(&", parentDataName, ");") + } else { + var fields []string + for j := 1; j <= i; j++ { + fields = append(fields, fmt.Sprintf("k%d", j)) + } + x.g.P(helper.Indent(ident), x.indexContainerName(index, i), "[{", strings.Join(fields, ", "), "}][", key, "].push_back(&", parentDataName, ");") + } + } +} + +func (x *Generator) genIndexSorter() { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + if len(index.SortedColFields) != 0 { + valueType := x.mapValueType(index) + x.g.P(helper.Indent(1), "// Index(sort): ", index.Index) + indexContainerName := x.indexContainerName(index, 0) + sorterDef := "auto " + indexContainerName + "sorter = [](" + x.g.P(helper.Indent(1), sorterDef, "const ", valueType, "* a,") + x.g.P(helper.Indent(1), helper.Whitespace(len(sorterDef)), "const ", valueType, "* b) {") + for i, field := range index.SortedColFields { + fieldName, _ := x.parseKeyFieldNameAndSuffix(field) + fieldName = strings.Replace(fieldName, ".", "->", 1) + if i == len(index.SortedColFields)-1 { + x.g.P(helper.Indent(2), "return a", fieldName, " < b", fieldName, ";") + } else { + x.g.P(helper.Indent(2), "if (a", fieldName, " != b", fieldName, ") {") + x.g.P(helper.Indent(3), "return a", fieldName, " < b", fieldName, ";") + x.g.P(helper.Indent(2), "}") + } + } + x.g.P(helper.Indent(1), "};") + x.g.P(helper.Indent(1), "for (auto&& item : ", indexContainerName, ") {") + x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(), ", indexContainerName, "sorter);") + x.g.P(helper.Indent(1), "}") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + x.g.P(helper.Indent(1), "for (auto&& item : ", x.indexContainerName(index, i), ") {") + x.g.P(helper.Indent(2), "for (auto&& item1 : item.second) {") + x.g.P(helper.Indent(3), "std::sort(item1.second.begin(), item1.second.end(), ", indexContainerName, "sorter);") + x.g.P(helper.Indent(2), "}") + x.g.P(helper.Indent(1), "}") + } + } + } + } +} + +func (x *Generator) genCppIndexFinders() { + if !x.needGenerateIndex() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + vectorType := x.indexMapValueVectorType(index) + mapType := x.indexMapType(index) + indexContainerName := x.indexContainerName(index, 0) + messagerName := x.messagerName() + + x.g.P("// Index: ", index.Index) + x.g.P("const ", messagerName, "::", mapType, "& ", messagerName, "::Find", index.Name(), "Map() const { return ", indexContainerName, " ;}") + x.g.P() + + keys := x.indexKeys(index) + params := keys.GenGetParams() + args := keys.GenGetArguments() + x.g.P("const ", messagerName, "::", vectorType, "* ", messagerName, "::Find", index.Name(), "(", params, ") const {") + if len(index.ColFields) == 1 { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find(", args, ");") + } else { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find({", args, "});") + } + x.g.P(helper.Indent(1), "if (iter == ", indexContainerName, ".end()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return &iter->second;") + x.g.P("}") + x.g.P() + + x.g.P("const ", x.mapValueType(index), "* ", messagerName, "::FindFirst", index.Name(), "(", params, ") const {") + x.g.P(helper.Indent(1), "auto conf = Find", index.Name(), "(", args, ");") + x.g.P(helper.Indent(1), "if (conf == nullptr || conf->empty()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return conf->front();") + x.g.P("}") + x.g.P() + + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + indexContainerName := x.indexContainerName(index, i) + partKeys := x.keys[:i] + partParams := partKeys.GenGetParams() + partArgs := partKeys.GenGetArguments() + x.g.P("const ", messagerName, "::", mapType, "* ", messagerName, "::Find", index.Name(), "Map(", partParams, ") const {") + if len(partKeys) == 1 { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find(", partArgs, ");") + } else { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find({", partArgs, "});") + } + x.g.P(helper.Indent(1), "if (iter == ", indexContainerName, ".end()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return &iter->second;") + x.g.P("}") + x.g.P() + + x.g.P("const ", messagerName, "::", vectorType, "* ", messagerName, "::Find", index.Name(), "(", partParams, ", ", params, ") const {") + x.g.P(helper.Indent(1), "auto map = Find", index.Name(), "Map(", partArgs, ");") + x.g.P(helper.Indent(1), "if (map == nullptr) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + if len(index.ColFields) == 1 { + x.g.P(helper.Indent(1), "auto iter = map->find(", args, ");") + } else { + x.g.P(helper.Indent(1), "auto iter = map->find({", args, "});") + } + x.g.P(helper.Indent(1), "if (iter == map->end()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return &iter->second;") + x.g.P("}") + x.g.P() + + x.g.P("const ", x.mapValueType(index), "* ", messagerName, "::FindFirst", index.Name(), "(", partParams, ", ", params, ") const {") + x.g.P(helper.Indent(1), "auto conf = Find", index.Name(), "(", partArgs, ", ", args, ");") + x.g.P(helper.Indent(1), "if (conf == nullptr || conf->empty()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return conf->front();") + x.g.P("}") + x.g.P() + } + } + } +} diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go new file mode 100644 index 00000000..365482fb --- /dev/null +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go @@ -0,0 +1,370 @@ +package indexes + +import ( + "fmt" + "strings" + "sync" + + "github.com/iancoleman/strcase" + "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" + "github.com/tableauio/loader/internal/index" + "github.com/tableauio/loader/internal/loadutil" + "github.com/tableauio/loader/internal/options" +) + +func (x *Generator) needGenerateOrderedIndex() bool { + return options.NeedGenOrderedIndex(x.message.Desc, options.LangCPP) +} + +func (x *Generator) orderedIndexMapType(index *index.LevelIndex) string { + return fmt.Sprintf("OrderedIndex_%sMap", index.Name()) +} + +func (x *Generator) orderedIndexMapKeyType(index *index.LevelIndex) string { + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + return helper.ParseOrderedIndexKeyType(field.FD) + } else { + // multi-column index + return fmt.Sprintf("OrderedIndex_%sKey", index.Name()) + } +} + +func (x *Generator) orderedIndexMapValueVectorType(index *index.LevelIndex) string { + return fmt.Sprintf("OrderedIndex_%sVector", index.Name()) +} + +func (x *Generator) orderedIndexContainerName(index *index.LevelIndex, i int) string { + if i == 0 { + return fmt.Sprintf("ordered_index_%s_map_", strcase.ToSnake(index.Name())) + } + return fmt.Sprintf("ordered_index_%s_map%d_", strcase.ToSnake(index.Name()), i) +} + +func (x *Generator) orderedIndexKeys(index *index.LevelIndex) helper.MapKeySlice { + var keys helper.MapKeySlice + for _, field := range index.ColFields { + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseOrderedIndexKeyType(field.FD), + Name: helper.ParseIndexFieldNameAsFuncParam(field.FD), + }) + } + return keys +} + +func (x *Generator) genHppOrderedIndexFinders() { + if !x.needGenerateOrderedIndex() { + return + } + var once sync.Once + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.g.P() + once.Do(func() { x.g.P(helper.Indent(1), "// OrderedIndex accessers.") }) + x.g.P(helper.Indent(1), "// OrderedIndex: ", index.Index) + x.g.P(" public:") + mapType := x.orderedIndexMapType(index) + keyType := x.orderedIndexMapKeyType(index) + vectorType := x.orderedIndexMapValueVectorType(index) + valueType := x.mapValueType(index) + keys := x.orderedIndexKeys(index) + if len(index.ColFields) != 1 { + // multi-column index + // Generate key struct + x.g.P(helper.Indent(1), "struct ", keyType, " {") + for _, key := range keys { + x.g.P(helper.Indent(2), key.Type, " ", key.Name, ";") + } + x.g.P("#if __cplusplus >= 202002L") + x.g.P(helper.Indent(2), "auto operator<=>(const ", keyType, "& other) const = default;") + x.g.P("#else") + x.g.P(helper.Indent(2), "bool operator<(const ", keyType, "& other) const {") + x.g.P(helper.Indent(3), "return std::tie(", keys.GenGetArguments(), ") < std::tie(", keys.GenOtherArguments("other"), ");") + x.g.P(helper.Indent(2), "}") + x.g.P("#endif") + x.g.P(helper.Indent(1), "};") + } + x.g.P(helper.Indent(1), "using ", vectorType, " = std::vector;") + x.g.P(helper.Indent(1), "using ", mapType, " = std::map<", keyType, ", ", vectorType, ">;") + x.g.P(helper.Indent(1), "// Finds the ordered index: key(", index.Index, ") to value(", vectorType, ") map.") + x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are represented by a vector.") + x.g.P(helper.Indent(1), "const ", mapType, "& Find", index.Name(), "Map() const;") + x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s).") + x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") + x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") + x.g.P(helper.Indent(1), "const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + partKeys := x.keys[:i] + x.g.P(helper.Indent(1), "// Finds the ordered index: key(", index.Index, ") to value(", vectorType, "),") + x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i), "-level map specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are represented by a vector.") + x.g.P(helper.Indent(1), "const ", mapType, "* Find", index.Name(), "Map(", partKeys.GenGetParams(), ") const;") + x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") + x.g.P(helper.Indent(1), "// Finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") + } + x.g.P() + + x.g.P(" private:") + x.g.P(helper.Indent(1), mapType, " ", x.orderedIndexContainerName(index, 0), ";") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + if i == 1 { + x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.orderedIndexContainerName(index, i), ";") + } else { + levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.orderedIndexContainerName(index, i), ";") + } + } + } + } +} + +func (x *Generator) genOrderedIndexLoader() { + if !x.needGenerateOrderedIndex() { + return + } + defer x.genOrderedIndexSorter() + x.g.P(helper.Indent(1), "// OrderedIndex init.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, 0), ".clear();") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, i), ".clear();") + } + } + } + parentDataName := "data_" + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.genOneCppOrderedIndexLoader(levelMessage.MapDepth, levelMessage.Depth, index, parentDataName) + } + itemName := fmt.Sprintf("item%d", levelMessage.Depth) + if levelMessage.FD == nil { + break + } + if !levelMessage.NextLevel.NeedGenOrderedIndex() { + break + } + x.g.P(helper.Indent(levelMessage.Depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") + parentDataName = itemName + if levelMessage.FD.IsMap() { + x.g.P(helper.Indent(levelMessage.Depth+1), "auto k", levelMessage.MapDepth, " = ", itemName, ".first;") + parentDataName = itemName + ".second" + } + defer x.g.P(helper.Indent(levelMessage.Depth), "}") + } +} + +func (x *Generator) genOneCppOrderedIndexLoader(depth int, ident int, index *index.LevelIndex, parentDataName string) { + x.g.P(helper.Indent(ident), "{") + x.g.P(helper.Indent(ident+1), "// OrderedIndex: ", index.Index) + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take the first field + fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) + if field.FD.IsList() { + itemName := fmt.Sprintf("item%d", depth) + x.g.P(helper.Indent(ident+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") + key := itemName + suffix + if field.FD.Enum() != nil { + key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" + } + x.genOrderedLoader(depth, ident+2, index, key, parentDataName) + x.g.P(helper.Indent(ident+1), "}") + } else { + key := parentDataName + fieldName + suffix + x.genOrderedLoader(depth, ident+1, index, key, parentDataName) + } + } else { + // multi-column index + x.generateOneCppMulticolumnOrderedIndex(depth, ident, index, parentDataName, nil) + } + x.g.P(helper.Indent(ident), "}") +} + +func (x *Generator) generateOneCppMulticolumnOrderedIndex(depth, ident int, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { + cursor := len(keys) + if cursor >= len(index.ColFields) { + keyType := x.orderedIndexMapKeyType(index) + x.g.P(helper.Indent(ident+1), keyType, " key{", keys.GenGetArguments(), "};") + x.genOrderedLoader(depth, ident+1, index, "key", parentDataName) + return + } + field := index.ColFields[cursor] + fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) + if field.FD.IsList() { + itemName := fmt.Sprintf("index_item%d", cursor) + x.g.P(helper.Indent(ident+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") + key := itemName + suffix + if field.FD.Enum() != nil { + key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" + } + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneCppMulticolumnOrderedIndex(depth, ident+1, index, parentDataName, keys) + x.g.P(helper.Indent(ident+1), "}") + } else { + key := parentDataName + fieldName + suffix + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneCppMulticolumnOrderedIndex(depth, ident, index, parentDataName, keys) + } +} + +func (x *Generator) genOrderedLoader(depth, ident int, index *index.LevelIndex, key, parentDataName string) { + x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") + for i := 1; i <= depth-2; i++ { + if i > len(x.keys) { + break + } + if i == 1 { + x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i), "[k1][", key, "].push_back(&", parentDataName, ");") + } else { + var fields []string + for j := 1; j <= i; j++ { + fields = append(fields, fmt.Sprintf("k%d", j)) + } + x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i), "[{", strings.Join(fields, ", "), "}][", key, "].push_back(&", parentDataName, ");") + } + } +} + +func (x *Generator) genOrderedIndexSorter() { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + if len(index.SortedColFields) != 0 { + valueType := x.mapValueType(index) + x.g.P(helper.Indent(1), "// OrderedIndex(sort): ", index.Index) + indexContainerName := x.orderedIndexContainerName(index, 0) + sorterDef := "auto " + indexContainerName + "sorter = [](" + x.g.P(helper.Indent(1), sorterDef, "const ", valueType, "* a,") + x.g.P(helper.Indent(1), helper.Whitespace(len(sorterDef)), "const ", valueType, "* b) {") + for i, field := range index.SortedColFields { + fieldName, _ := x.parseKeyFieldNameAndSuffix(field) + fieldName = strings.Replace(fieldName, ".", "->", 1) + if i == len(index.SortedColFields)-1 { + x.g.P(helper.Indent(2), "return a", fieldName, " < b", fieldName, ";") + } else { + x.g.P(helper.Indent(2), "if (a", fieldName, " != b", fieldName, ") {") + x.g.P(helper.Indent(3), "return a", fieldName, " < b", fieldName, ";") + x.g.P(helper.Indent(2), "}") + } + } + x.g.P(helper.Indent(1), "};") + x.g.P(helper.Indent(1), "for (auto&& item : ", indexContainerName, ") {") + x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(), ", indexContainerName, "sorter);") + x.g.P(helper.Indent(1), "}") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + x.g.P(helper.Indent(1), "for (auto&& item : ", x.orderedIndexContainerName(index, i), ") {") + x.g.P(helper.Indent(2), "for (auto&& item1 : item.second) {") + x.g.P(helper.Indent(3), "std::sort(item1.second.begin(), item1.second.end(), ", indexContainerName, "sorter);") + x.g.P(helper.Indent(2), "}") + x.g.P(helper.Indent(1), "}") + } + } + } + } +} + +func (x *Generator) genCppOrderedIndexFinders() { + if !x.needGenerateOrderedIndex() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + vectorType := x.orderedIndexMapValueVectorType(index) + mapType := x.orderedIndexMapType(index) + indexContainerName := x.orderedIndexContainerName(index, 0) + messagerName := x.messagerName() + + x.g.P("// OrderedIndex: ", index.Index) + x.g.P("const ", messagerName, "::", mapType, "& ", messagerName, "::Find", index.Name(), "Map() const { return ", indexContainerName, " ;}") + x.g.P() + + keys := x.orderedIndexKeys(index) + params := keys.GenGetParams() + args := keys.GenGetArguments() + x.g.P("const ", messagerName, "::", vectorType, "* ", messagerName, "::Find", index.Name(), "(", params, ") const {") + if len(index.ColFields) == 1 { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find(", args, ");") + } else { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find({", args, "});") + } + x.g.P(helper.Indent(1), "if (iter == ", indexContainerName, ".end()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return &iter->second;") + x.g.P("}") + x.g.P() + + x.g.P("const ", x.mapValueType(index), "* ", messagerName, "::FindFirst", index.Name(), "(", params, ") const {") + x.g.P(helper.Indent(1), "auto conf = Find", index.Name(), "(", args, ");") + x.g.P(helper.Indent(1), "if (conf == nullptr || conf->empty()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return conf->front();") + x.g.P("}") + x.g.P() + + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + indexContainerNameI := x.orderedIndexContainerName(index, i) + partKeys := x.keys[:i] + partParams := partKeys.GenGetParams() + partArgs := partKeys.GenGetArguments() + x.g.P("const ", messagerName, "::", mapType, "* ", messagerName, "::Find", index.Name(), "Map(", partParams, ") const {") + if len(partKeys) == 1 { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerNameI, ".find(", partArgs, ");") + } else { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerNameI, ".find({", partArgs, "});") + } + x.g.P(helper.Indent(1), "if (iter == ", indexContainerNameI, ".end()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return &iter->second;") + x.g.P("}") + x.g.P() + + x.g.P("const ", messagerName, "::", vectorType, "* ", messagerName, "::Find", index.Name(), "(", partParams, ", ", params, ") const {") + x.g.P(helper.Indent(1), "auto map = Find", index.Name(), "Map(", partArgs, ");") + x.g.P(helper.Indent(1), "if (map == nullptr) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + if len(index.ColFields) == 1 { + x.g.P(helper.Indent(1), "auto iter = map->find(", args, ");") + } else { + x.g.P(helper.Indent(1), "auto iter = map->find({", args, "});") + } + x.g.P(helper.Indent(1), "if (iter == map->end()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return &iter->second;") + x.g.P("}") + x.g.P() + + x.g.P("const ", x.mapValueType(index), "* ", messagerName, "::FindFirst", index.Name(), "(", partParams, ", ", params, ") const {") + x.g.P(helper.Indent(1), "auto conf = Find", index.Name(), "(", partArgs, ", ", args, ");") + x.g.P(helper.Indent(1), "if (conf == nullptr || conf->empty()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return conf->front();") + x.g.P("}") + x.g.P() + } + } + } +} diff --git a/cmd/protoc-gen-cpp-tableau-loader/messager.go b/cmd/protoc-gen-cpp-tableau-loader/messager.go index a9f6f0a3..db1c9eaa 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/messager.go +++ b/cmd/protoc-gen-cpp-tableau-loader/messager.go @@ -4,8 +4,7 @@ import ( "strings" "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" - idx "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/index" - "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/orderedindex" + "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/indexes" "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/orderedmap" "github.com/tableauio/loader/internal/extensions" "github.com/tableauio/loader/internal/index" @@ -85,8 +84,7 @@ func genHppMessage(g *protogen.GeneratedFile, message *protogen.Message) { indexDescriptor := index.ParseIndexDescriptor(message.Desc) orderedMapGenerator := orderedmap.NewGenerator(g, message) - indexGenerator := idx.NewGenerator(g, indexDescriptor, message) - orderedIndexGenerator := orderedindex.NewGenerator(g, indexDescriptor, message) + indexGenerator := indexes.NewGenerator(g, indexDescriptor, message) g.P("class ", message.Desc.Name(), " : public Messager {") g.P(" public:") @@ -96,7 +94,7 @@ func genHppMessage(g *protogen.GeneratedFile, message *protogen.Message) { g.P(helper.Indent(1), "const google::protobuf::Message* Message() const override { return &data_; }") g.P() - if orderedMapGenerator.NeedGenerate() || indexGenerator.NeedGenerate() || orderedIndexGenerator.NeedGenerate() { + if orderedMapGenerator.NeedGenerate() || indexGenerator.NeedGenerate() { g.P(" private:") g.P(helper.Indent(1), "virtual bool ProcessAfterLoad() override final;") g.P() @@ -110,12 +108,11 @@ func genHppMessage(g *protogen.GeneratedFile, message *protogen.Message) { g.P(helper.Indent(1), cppFullName, " data_;") orderedMapGenerator.GenHppOrderedMapGetters() indexGenerator.GenHppIndexFinders() - orderedIndexGenerator.GenHppOrderedIndexFinders() g.P("};") g.P() } -func genHppMapGetters(depth int, keys helper.MapKeys, g *protogen.GeneratedFile, md protoreflect.MessageDescriptor) { +func genHppMapGetters(depth int, keys helper.MapKeySlice, g *protogen.GeneratedFile, md protoreflect.MessageDescriptor) { for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) if fd.IsMap() { @@ -161,8 +158,7 @@ func genCppMessage(g *protogen.GeneratedFile, message *protogen.Message) { indexDescriptor := index.ParseIndexDescriptor(message.Desc) orderedMapGenerator := orderedmap.NewGenerator(g, message) - indexGenerator := idx.NewGenerator(g, indexDescriptor, message) - orderedIndexGenerator := orderedindex.NewGenerator(g, indexDescriptor, message) + indexGenerator := indexes.NewGenerator(g, indexDescriptor, message) g.P("const std::string ", messagerName, "::kProtoName = ", cppFullName, `::GetDescriptor()->name();`) g.P() @@ -175,11 +171,10 @@ func genCppMessage(g *protogen.GeneratedFile, message *protogen.Message) { g.P("}") g.P() - if orderedMapGenerator.NeedGenerate() || indexGenerator.NeedGenerate() || orderedIndexGenerator.NeedGenerate() { + if orderedMapGenerator.NeedGenerate() || indexGenerator.NeedGenerate() { g.P("bool ", messagerName, "::ProcessAfterLoad() {") orderedMapGenerator.GenOrderedMapLoader() - indexGenerator.GenCppIndexLoader() - orderedIndexGenerator.GenCppOrderedIndexLoader() + indexGenerator.GenIndexLoader() g.P(helper.Indent(1), "return true;") g.P("}") g.P() @@ -189,10 +184,9 @@ func genCppMessage(g *protogen.GeneratedFile, message *protogen.Message) { genCppMapGetters(g, message.Desc, 1, nil, messagerName) orderedMapGenerator.GenOrderedMapGetters() indexGenerator.GenCppIndexFinders() - orderedIndexGenerator.GenCppOrderedIndexFinders() } -func genCppMapGetters(g *protogen.GeneratedFile, md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys, messagerName string) { +func genCppMapGetters(g *protogen.GeneratedFile, md protoreflect.MessageDescriptor, depth int, keys helper.MapKeySlice, messagerName string) { for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) if fd.IsMap() { diff --git a/cmd/protoc-gen-cpp-tableau-loader/orderedindex/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/orderedindex/ordered_index.go deleted file mode 100644 index 7650a389..00000000 --- a/cmd/protoc-gen-cpp-tableau-loader/orderedindex/ordered_index.go +++ /dev/null @@ -1,306 +0,0 @@ -package orderedindex - -import ( - "fmt" - "strings" - - "github.com/iancoleman/strcase" - "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" - "github.com/tableauio/loader/internal/index" - "github.com/tableauio/loader/internal/options" - "google.golang.org/protobuf/compiler/protogen" - "google.golang.org/protobuf/reflect/protoreflect" -) - -type Generator struct { - g *protogen.GeneratedFile - descriptor *index.IndexDescriptor - message *protogen.Message -} - -func NewGenerator(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { - return &Generator{ - g: g, - descriptor: descriptor, - message: message, - } -} - -func (x *Generator) NeedGenerate() bool { - return options.NeedGenOrderedIndex(x.message.Desc, options.LangCPP) -} - -func (x *Generator) messagerName() string { - return string(x.message.Desc.Name()) -} - -func (x *Generator) mapType(index *index.LevelIndex) string { - return fmt.Sprintf("OrderedIndex_%sMap", index.Name()) -} - -func (x *Generator) mapKeyType(index *index.LevelIndex) string { - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take first field - return helper.ParseOrderedIndexKeyType(field.FD) - } else { - // multi-column index - return fmt.Sprintf("OrderedIndex_%sKey", index.Name()) - } -} - -func (x *Generator) mapValueType(index *index.LevelIndex) string { - return helper.ParseCppClassType(index.MD) -} - -func (x *Generator) mapValueVectorType(index *index.LevelIndex) string { - return fmt.Sprintf("OrderedIndex_%sVector", index.Name()) -} - -func (x *Generator) indexContainerName(index *index.LevelIndex) string { - return fmt.Sprintf("ordered_index_%s_map_", strcase.ToSnake(index.Name())) -} - -func (x *Generator) indexKeys(index *index.LevelIndex) helper.MapKeys { - var keys helper.MapKeys - for _, field := range index.ColFields { - keys = keys.AddMapKey(helper.MapKey{ - Type: helper.ParseOrderedIndexKeyType(field.FD), - Name: helper.ParseIndexFieldNameAsFuncParam(field.FD), - }) - } - return keys -} - -func (x *Generator) fieldGetter(fd protoreflect.FieldDescriptor) string { - return fmt.Sprintf(".%s()", helper.ParseIndexFieldName(fd)) -} - -func (x *Generator) parseKeyFieldNameAndSuffix(field *index.LevelField) (string, string) { - var fieldName, suffix string - for i, leveledFd := range field.LeveledFDList { - fieldName += x.fieldGetter(leveledFd) - if i == len(field.LeveledFDList)-1 && leveledFd.Message() != nil { - switch leveledFd.Message().FullName() { - case "google.protobuf.Timestamp", "google.protobuf.Duration": - suffix = ".seconds()" - default: - } - } - } - return fieldName, suffix -} - -func (x *Generator) GenHppOrderedIndexFinders() { - if !x.NeedGenerate() { - return - } - x.g.P(helper.Indent(1), "// OrderedIndex accessers.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - x.g.P(helper.Indent(1), "// OrderedIndex: ", index.Index) - x.g.P(" public:") - mapType := x.mapType(index) - keyType := x.mapKeyType(index) - vectorType := x.mapValueVectorType(index) - valueType := x.mapValueType(index) - keys := x.indexKeys(index) - if len(index.ColFields) != 1 { - // multi-column index - // Generate key struct - x.g.P(helper.Indent(1), "struct ", keyType, " {") - for _, key := range keys { - x.g.P(helper.Indent(2), key.Type, " ", key.Name, ";") - } - x.g.P("#if __cplusplus >= 202002L") - x.g.P(helper.Indent(2), "auto operator<=>(const ", keyType, "& other) const = default;") - x.g.P("#else") - x.g.P(helper.Indent(2), "bool operator<(const ", keyType, "& other) const {") - if len(keys) == 1 { - x.g.P(helper.Indent(3), "return ", keys[0].Name, " < other.", keys[0].Name, ";") - } else { - x.g.P(helper.Indent(3), "return std::tie(", keys.GenGetArguments(), ") < std::tie(", keys.GenOtherArguments("other"), ");") - } - x.g.P(helper.Indent(2), "}") - x.g.P("#endif") - x.g.P(helper.Indent(1), "};") - } - x.g.P(helper.Indent(1), "using ", vectorType, " = std::vector;") - x.g.P(helper.Indent(1), "using ", mapType, " = std::map<", keyType, ", ", vectorType, ">;") - x.g.P(helper.Indent(1), "// Finds the ordered index (", index.Index, ") to value (", vectorType, ") map.") - x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are contained by a vector.") - x.g.P(helper.Indent(1), "const ", mapType, "& Find", index.Name(), "() const;") - x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s).") - x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") - x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") - x.g.P(helper.Indent(1), "const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") - x.g.P() - - x.g.P(" private:") - x.g.P(helper.Indent(1), mapType, " ", x.indexContainerName(index), ";") - x.g.P() - } - } -} - -func (x *Generator) GenCppOrderedIndexLoader() { - if !x.NeedGenerate() { - return - } - x.g.P(helper.Indent(1), "// OrderedIndex init.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - x.g.P(helper.Indent(1), x.indexContainerName(index), ".clear();") - } - } - parentDataName := "data_" - depth := 1 - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - x.genOneCppOrderedIndexLoader(depth, index, parentDataName) - } - itemName := fmt.Sprintf("item%d", depth) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGen() { - break - } - x.g.P(helper.Indent(depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") - parentDataName = itemName - if levelMessage.FD.IsMap() { - parentDataName = itemName + ".second" - } - depth++ - } - for i := depth - 1; i > 0; i-- { - x.g.P(helper.Indent(i), "}") - } - x.genOrderedIndexSorter() -} - -func (x *Generator) genOneCppOrderedIndexLoader(depth int, index *index.LevelIndex, parentDataName string) { - x.g.P(helper.Indent(depth), "{") - x.g.P(helper.Indent(depth+1), "// OrderedIndex: ", index.Index) - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take the first field - fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) - indexContainerName := x.indexContainerName(index) - if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) - x.g.P(helper.Indent(depth+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") - key := itemName + suffix - if field.FD.Enum() != nil { - key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" - } - x.g.P(helper.Indent(depth+2), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") - x.g.P(helper.Indent(depth+1), "}") - } else { - key := parentDataName + fieldName + suffix - x.g.P(helper.Indent(depth+1), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") - } - } else { - // multi-column index - x.generateOneCppMulticolumnOrderedIndex(depth, index, parentDataName, nil) - } - x.g.P(helper.Indent(depth), "}") -} - -func (x *Generator) generateOneCppMulticolumnOrderedIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeys) { - cursor := len(keys) - if cursor >= len(index.ColFields) { - keyType := x.mapKeyType(index) - indexContainerName := x.indexContainerName(index) - x.g.P(helper.Indent(depth+1), keyType, " key{", keys.GenGetArguments(), "};") - x.g.P(helper.Indent(depth+1), indexContainerName, "[key].push_back(&", parentDataName, ");") - return - } - field := index.ColFields[cursor] - fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) - if field.FD.IsList() { - itemName := fmt.Sprintf("index_item%d", cursor) - x.g.P(helper.Indent(depth+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") - key := itemName + suffix - if field.FD.Enum() != nil { - key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" - } - keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnOrderedIndex(depth+1, index, parentDataName, keys) - x.g.P(helper.Indent(depth+1), "}") - } else { - key := parentDataName + fieldName + suffix - keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnOrderedIndex(depth, index, parentDataName, keys) - } -} - -func (x *Generator) genOrderedIndexSorter() { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - if len(index.SortedColFields) != 0 { - valueType := x.mapValueType(index) - x.g.P(helper.Indent(1), "// OrderedIndex(sort): ", index.Index) - x.g.P(helper.Indent(1), "for (auto&& item : ", x.indexContainerName(index), ") {") - x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(),") - x.g.P(helper.Indent(7), "[](const ", valueType, "* a, const ", valueType, "* b) {") - for i, field := range index.SortedColFields { - fieldName, _ := x.parseKeyFieldNameAndSuffix(field) - fieldName = strings.Replace(fieldName, ".", "->", 1) - if i == len(index.SortedColFields)-1 { - x.g.P(helper.Indent(8), "return a", fieldName, " < b", fieldName, ";") - } else { - x.g.P(helper.Indent(8), "if (a", fieldName, " != b", fieldName, ") {") - x.g.P(helper.Indent(9), "return a", fieldName, " < b", fieldName, ";") - x.g.P(helper.Indent(8), "}") - } - } - x.g.P(helper.Indent(7), "});") - x.g.P(helper.Indent(1), "}") - } - } - } -} - -func (x *Generator) GenCppOrderedIndexFinders() { - if !x.NeedGenerate() { - return - } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - vectorType := x.mapValueVectorType(index) - mapType := x.mapType(index) - indexContainerName := x.indexContainerName(index) - messagerName := x.messagerName() - - x.g.P("// OrderedIndex: ", index.Index) - x.g.P("const ", messagerName, "::", mapType, "& ", messagerName, "::Find", index.Name(), "() const { return ", indexContainerName, " ;}") - x.g.P() - - keys := x.indexKeys(index) - params := keys.GenGetParams() - args := keys.GenGetArguments() - x.g.P("const ", messagerName, "::", vectorType, "* ", messagerName, "::Find", index.Name(), "(", params, ") const {") - if len(index.ColFields) == 1 { - x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find(", args, ");") - } else { - x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find({", args, "});") - } - x.g.P(helper.Indent(1), "if (iter == ", indexContainerName, ".end()) {") - x.g.P(helper.Indent(2), "return nullptr;") - x.g.P(helper.Indent(1), "}") - x.g.P(helper.Indent(1), "return &iter->second;") - x.g.P("}") - x.g.P() - - x.g.P("const ", x.mapValueType(index), "* ", messagerName, "::FindFirst", index.Name(), "(", params, ") const {") - x.g.P(helper.Indent(1), "auto conf = Find", index.Name(), "(", args, ");") - x.g.P(helper.Indent(1), "if (conf == nullptr || conf->empty()) {") - x.g.P(helper.Indent(2), "return nullptr;") - x.g.P(helper.Indent(1), "}") - x.g.P(helper.Indent(1), "return conf->front();") - x.g.P("}") - x.g.P() - } - } -} diff --git a/cmd/protoc-gen-cpp-tableau-loader/orderedmap/ordered_map.go b/cmd/protoc-gen-cpp-tableau-loader/orderedmap/ordered_map.go index ad51515e..c833ef65 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/orderedmap/ordered_map.go +++ b/cmd/protoc-gen-cpp-tableau-loader/orderedmap/ordered_map.go @@ -2,7 +2,6 @@ package orderedmap import ( "fmt" - "strings" "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" "github.com/tableauio/loader/internal/options" @@ -30,20 +29,12 @@ func (x *Generator) messagerName() string { return string(x.message.Desc.Name()) } -func (x *Generator) orderedMapPrefix(mapFd protoreflect.FieldDescriptor) string { - if mapFd.MapValue().Kind() == protoreflect.MessageKind { - localMsgProtoName := strings.TrimPrefix(string(mapFd.MapValue().Message().FullName()), string(x.message.Desc.FullName())+".") - return strings.ReplaceAll(localMsgProtoName, ".", "_") - } - return mapFd.MapValue().Kind().String() -} - func (x *Generator) mapType(mapFd protoreflect.FieldDescriptor) string { - return fmt.Sprintf("OrderedMap_%sMap", x.orderedMapPrefix(mapFd)) + return fmt.Sprintf("OrderedMap_%sMap", helper.ParseLeveledMapPrefix(x.message.Desc, mapFd)) } func (x *Generator) mapValueType(mapFd protoreflect.FieldDescriptor) string { - return fmt.Sprintf("OrderedMap_%sValue", x.orderedMapPrefix(mapFd)) + return fmt.Sprintf("OrderedMap_%sValue", helper.ParseLeveledMapPrefix(x.message.Desc, mapFd)) } func (x *Generator) mapValueFieldType(fd protoreflect.FieldDescriptor) string { @@ -64,7 +55,7 @@ func (x *Generator) GenHppOrderedMapGetters() { x.genHppOrderedMapGetters(x.message.Desc, 1, nil) } -func (x *Generator) genHppOrderedMapGetters(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys) { +func (x *Generator) genHppOrderedMapGetters(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeySlice) { for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) if fd.IsMap() { @@ -150,7 +141,7 @@ func (x *Generator) GenOrderedMapGetters() { x.genOrderedMapGetters(x.message.Desc, 1, nil) } -func (x *Generator) genOrderedMapGetters(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys) { +func (x *Generator) genOrderedMapGetters(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeySlice) { messagerName := x.messagerName() for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) diff --git a/cmd/protoc-gen-go-tableau-loader/helper/helper.go b/cmd/protoc-gen-go-tableau-loader/helper/helper.go index 669cca52..79fc79ad 100644 --- a/cmd/protoc-gen-go-tableau-loader/helper/helper.go +++ b/cmd/protoc-gen-go-tableau-loader/helper/helper.go @@ -37,7 +37,7 @@ func ParseIndexFieldNameAsFuncParam(gen *protogen.Plugin, fd protoreflect.FieldD if fieldName == "" { return fieldName } - return escapeIdentifier(strings.ToLower(fieldName[:1]) + fieldName[1:]) + return escapeIdentifier(fieldName) } // ParseGoType converts a FieldDescriptor to its Go type. @@ -130,7 +130,7 @@ func ParseOrderedIndexKeyType(gen *protogen.Plugin, g *protogen.GeneratedFile, f } } -func ParseMapFieldName(fd protoreflect.FieldDescriptor) string { +func ParseMapFieldNameAsKeyStructFieldName(fd protoreflect.FieldDescriptor) string { opts := fd.Options().(*descriptorpb.FieldOptions) fdOpts := proto.GetExtension(opts, tableaupb.E_Field).(*tableaupb.FieldOptions) name := fdOpts.GetKey() @@ -138,7 +138,15 @@ func ParseMapFieldName(fd protoreflect.FieldDescriptor) string { valueFd := fd.MapValue().Message().Fields().Get(0) name = string(valueFd.Name()) } - return escapeIdentifier(name) + return strcase.ToCamel(name) +} + +func ParseMapFieldNameAsFuncParam(fd protoreflect.FieldDescriptor) string { + fieldName := ParseMapFieldNameAsKeyStructFieldName(fd) + if fieldName == "" { + return fieldName + } + return escapeIdentifier(fieldName) } func ParseMapValueType(gen *protogen.Plugin, g *protogen.GeneratedFile, fd protoreflect.FieldDescriptor) string { @@ -242,41 +250,49 @@ func GetTypeEmptyValue(fd protoreflect.FieldDescriptor) string { } } +func ParseLeveledMapPrefix(md protoreflect.MessageDescriptor, mapFd protoreflect.FieldDescriptor) string { + if mapFd.MapValue().Kind() == protoreflect.MessageKind { + localMsgProtoName := strings.TrimPrefix(string(mapFd.MapValue().Message().FullName()), string(md.FullName())+".") + return strings.ReplaceAll(localMsgProtoName, ".", "_") + } + return mapFd.MapValue().Kind().String() +} + type MapKey struct { Type string Name string FieldName string // multi-colunm index only } -type MapKeys []MapKey +type MapKeySlice []MapKey -func (keys MapKeys) AddMapKey(newKey MapKey) MapKeys { +func (s MapKeySlice) AddMapKey(newKey MapKey) MapKeySlice { if newKey.Name == "" { - newKey.Name = fmt.Sprintf("key%d", len(keys)+1) + newKey.Name = fmt.Sprintf("key%d", len(s)+1) } - for _, key := range keys { + for _, key := range s { if key.Name == newKey.Name { // rewrite to avoid name confict - newKey.Name = fmt.Sprintf("%s%d", newKey.Name, len(keys)+1) + newKey.Name = fmt.Sprintf("%s%d", newKey.Name, len(s)+1) break } } - return append(keys, newKey) + return append(s, newKey) } // GenGetParams generates function parameters, which are the names listed in the function's definition. -func (keys MapKeys) GenGetParams() string { +func (s MapKeySlice) GenGetParams() string { var params []string - for _, key := range keys { + for _, key := range s { params = append(params, key.Name+" "+key.Type) } return strings.Join(params, ", ") } // GenGetArguments generates function arguments, which are the real values passed to the function. -func (keys MapKeys) GenGetArguments() string { +func (s MapKeySlice) GenGetArguments() string { var params []string - for _, key := range keys { + for _, key := range s { params = append(params, key.Name) } return strings.Join(params, ", ") diff --git a/cmd/protoc-gen-go-tableau-loader/index/index.go b/cmd/protoc-gen-go-tableau-loader/index/index.go deleted file mode 100644 index 03278700..00000000 --- a/cmd/protoc-gen-go-tableau-loader/index/index.go +++ /dev/null @@ -1,272 +0,0 @@ -package index - -import ( - "fmt" - - "github.com/iancoleman/strcase" - "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" - "github.com/tableauio/loader/internal/index" - "github.com/tableauio/loader/internal/options" - "google.golang.org/protobuf/compiler/protogen" - "google.golang.org/protobuf/reflect/protoreflect" -) - -type Generator struct { - gen *protogen.Plugin - g *protogen.GeneratedFile - descriptor *index.IndexDescriptor - message *protogen.Message -} - -func NewGenerator(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { - return &Generator{ - gen: gen, - g: g, - descriptor: descriptor, - message: message, - } -} - -func (x *Generator) needGenerate() bool { - return options.NeedGenIndex(x.message.Desc, options.LangGO) -} - -func (x *Generator) messagerName() string { - return string(x.message.Desc.Name()) -} - -func (x *Generator) mapType(index *index.LevelIndex) string { - return fmt.Sprintf("%s_Index_%sMap", x.messagerName(), index.Name()) -} - -func (x *Generator) mapKeyType(index *index.LevelIndex) string { - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take first field - return helper.ParseGoType(x.gen, x.g, field.FD) - } else { - // multi-column index - return fmt.Sprintf("%s_Index_%sKey", x.messagerName(), index.Name()) - } -} - -func (x *Generator) mapValueType(index *index.LevelIndex) protogen.GoIdent { - return helper.FindMessageGoIdent(x.gen, index.MD) -} - -func (x *Generator) indexContainerName(index *index.LevelIndex) string { - return fmt.Sprintf("index%sMap", strcase.ToCamel(index.Name())) -} - -func (x *Generator) indexKeys(index *index.LevelIndex) helper.MapKeys { - var keys helper.MapKeys - for _, field := range index.ColFields { - keys = keys.AddMapKey(helper.MapKey{ - Type: helper.ParseGoType(x.gen, x.g, field.FD), - Name: helper.ParseIndexFieldNameAsFuncParam(x.gen, field.FD), - FieldName: helper.ParseIndexFieldNameAsKeyStructFieldName(x.gen, field.FD), - }) - } - return keys -} - -func (x *Generator) fieldGetter(fd protoreflect.FieldDescriptor) string { - return fmt.Sprintf(".Get%s()", helper.ParseIndexFieldName(x.gen, fd)) -} - -func (x *Generator) parseKeyFieldName(field *index.LevelField) string { - var fieldName string - for _, leveledFd := range field.LeveledFDList { - fieldName += x.fieldGetter(leveledFd) - } - return fieldName -} - -func (x *Generator) GenIndexTypeDef() { - if !x.needGenerate() { - return - } - x.g.P("// Index types.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - x.g.P("// Index: ", index.Index) - if len(index.ColFields) != 1 { - // multi-column index - keyType := x.mapKeyType(index) - keys := x.indexKeys(index) - - // generate key struct - // KeyType must be comparable, refer https://go.dev/blog/maps - x.g.P("type ", keyType, " struct {") - for _, key := range keys { - x.g.P(key.FieldName, " ", key.Type) - } - x.g.P("}") - } - x.g.P("type ", x.mapType(index), " = map[", x.mapKeyType(index), "][]*", x.mapValueType(index)) - x.g.P() - } - } -} - -func (x *Generator) GenIndexField() { - if !x.needGenerate() { - return - } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - x.g.P(x.indexContainerName(index), " ", x.mapType(index)) - } - } -} - -func (x *Generator) GenIndexLoader() { - if !x.needGenerate() { - return - } - x.g.P("// Index init.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - x.g.P("x.", x.indexContainerName(index), " = make(", x.mapType(index), ")") - } - } - parentDataName := "x.data" - depth := 1 - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - x.genOneIndexLoader(index, depth, parentDataName) - } - itemName := fmt.Sprintf("item%d", depth) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGen() { - break - } - x.g.P("for _, ", itemName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") - parentDataName = itemName - depth++ - } - for i := depth - 1; i > 0; i-- { - x.g.P("}") - } - x.genIndexSorter() -} - -func (x *Generator) genOneIndexLoader(index *index.LevelIndex, depth int, parentDataName string) { - x.g.P("{") - x.g.P("// Index: ", index.Index) - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take the first field - fieldName := x.parseKeyFieldName(field) - indexContainerName := x.indexContainerName(index) - if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) - x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") - x.g.P("key := ", itemName) - x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") - x.g.P("}") - } else { - x.g.P("key := ", parentDataName, fieldName) - x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") - } - } else { - // multi-column index - x.generateOneMulticolumnIndex(depth, index, parentDataName, nil) - } - x.g.P("}") -} - -func (x *Generator) generateOneMulticolumnIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeys) { - cursor := len(keys) - if cursor >= len(index.ColFields) { - keyType := x.mapKeyType(index) - indexContainerName := x.indexContainerName(index) - x.g.P("key := ", keyType, " {", keys.GenGetArguments(), "}") - x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") - return - } - field := index.ColFields[cursor] - fieldName := x.parseKeyFieldName(field) - if field.FD.IsList() { - itemName := fmt.Sprintf("indexItem%d", cursor) - x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") - keys = keys.AddMapKey(helper.MapKey{Name: itemName}) - x.generateOneMulticolumnIndex(depth+1, index, parentDataName, keys) - x.g.P("}") - } else { - key := parentDataName + fieldName - keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneMulticolumnIndex(depth, index, parentDataName, keys) - } -} - -func (x *Generator) genIndexSorter() { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - if len(index.SortedColFields) != 0 { - x.g.P("// Index(sort): ", index.Index) - x.g.P("for _, item := range x.", x.indexContainerName(index), " {") - x.g.P(helper.SortPackage.Ident("Slice"), "(item, func(i, j int) bool {") - for i, field := range index.SortedColFields { - fieldName := x.parseKeyFieldName(field) - if i == len(index.SortedColFields)-1 { - x.g.P("return item[i]", fieldName, " < item[j]", fieldName) - } else { - x.g.P("if item[i]", fieldName, " != item[j]", fieldName, " {") - x.g.P("return item[i]", fieldName, " < item[j]", fieldName) - x.g.P("}") - } - } - x.g.P("})") - x.g.P("}") - } - } - } -} - -func (x *Generator) GenIndexFinders() { - if !x.needGenerate() { - return - } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - indexContainerName := x.indexContainerName(index) - messagerName := x.messagerName() - x.g.P("// Index: ", index.Index) - x.g.P() - - x.g.P("// Find", index.Name(), "Map finds the index (", index.Index, ") to value (", x.mapValueType(index), ") map.") - x.g.P("// One key may correspond to multiple values, which are contained by a slice.") - x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map() ", x.mapType(index), " {") - x.g.P("return x.", indexContainerName) - x.g.P("}") - x.g.P() - - keys := x.indexKeys(index) - params := keys.GenGetParams() - args := keys.GenGetArguments() - x.g.P("// Find", index.Name(), " finds a slice of all values of the given key.") - x.g.P("func (x *", messagerName, ") Find", index.Name(), "(", params, ") []*", x.mapValueType(index), " {") - if len(index.ColFields) == 1 { - x.g.P("return x.", indexContainerName, "[", args, "]") - } else { - x.g.P("return x.", indexContainerName, "[", x.mapKeyType(index), "{", args, "}]") - } - x.g.P("}") - x.g.P() - - x.g.P("// FindFirst", index.Name(), " finds the first value of the given key,") - x.g.P("// or nil if no value found.") - x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), "(", params, ") *", x.mapValueType(index), " {") - x.g.P("val := x.Find", index.Name(), "(", args, ")") - x.g.P("if len(val) > 0 {") - x.g.P("return val[0]") - x.g.P("}") - x.g.P("return nil") - x.g.P("}") - x.g.P() - } - } -} diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go new file mode 100644 index 00000000..6ce60493 --- /dev/null +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -0,0 +1,121 @@ +package indexes + +import ( + "fmt" + + "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" + "github.com/tableauio/loader/internal/index" + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type Generator struct { + gen *protogen.Plugin + g *protogen.GeneratedFile + descriptor *index.IndexDescriptor + message *protogen.Message + + // level message + maxDepth int + keys helper.MapKeySlice + mapFds []protoreflect.FieldDescriptor +} + +func NewGenerator(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { + generator := &Generator{ + gen: gen, + g: g, + descriptor: descriptor, + message: message, + } + generator.initLevelMessage() + return generator +} + +func (x *Generator) initLevelMessage() { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + if fd := levelMessage.FD; fd != nil && fd.IsMap() { + x.keys = x.keys.AddMapKey(helper.MapKey{ + Type: helper.ParseMapKeyType(fd.MapKey()), + Name: helper.ParseMapFieldNameAsFuncParam(fd), + FieldName: helper.ParseMapFieldNameAsKeyStructFieldName(fd), + }) + x.mapFds = append(x.mapFds, fd) + } + if len(levelMessage.Indexes) != 0 || len(levelMessage.OrderedIndexes) != 0 { + x.maxDepth = levelMessage.MapDepth + } + } +} + +func (x *Generator) NeedGenerate() bool { + return x.needGenerateIndex() || x.needGenerateOrderedIndex() +} + +func (x *Generator) messagerName() string { + return string(x.message.Desc.Name()) +} + +func (x *Generator) levelKeyType(mapFd protoreflect.FieldDescriptor) string { + return fmt.Sprintf("%s_LevelIndex_%sKey", x.messagerName(), helper.ParseLeveledMapPrefix(x.message.Desc, mapFd)) +} + +func (x *Generator) mapValueType(index *index.LevelIndex) protogen.GoIdent { + return helper.FindMessageGoIdent(x.gen, index.MD) +} + +func (x *Generator) fieldGetter(fd protoreflect.FieldDescriptor) string { + return fmt.Sprintf(".Get%s()", helper.ParseIndexFieldName(x.gen, fd)) +} + +func (x *Generator) parseKeyFieldNameAndSuffix(field *index.LevelField) (string, string) { + var fieldName, suffix string + for i, leveledFd := range field.LeveledFDList { + fieldName += x.fieldGetter(leveledFd) + if i == len(field.LeveledFDList)-1 && leveledFd.Message() != nil { + switch leveledFd.Message().FullName() { + case "google.protobuf.Timestamp", "google.protobuf.Duration": + suffix = ".GetSeconds()" + default: + } + } + } + return fieldName, suffix +} + +func (x *Generator) GenIndexTypeDef() { + if !x.NeedGenerate() { + return + } + for i := 1; i <= x.maxDepth-3 && i <= len(x.mapFds)-1; i++ { + if i == 1 { + x.g.P() + x.g.P("// LevelIndex keys.") + } + fd := x.mapFds[i] + keyType := x.levelKeyType(fd) + keys := x.keys[:i+1] + x.g.P("type ", keyType, " struct {") + for _, key := range keys { + x.g.P(key.FieldName, " ", key.Type) + } + x.g.P("}") + } + x.genIndexTypeDef() + x.genOrderedIndexTypeDef() +} + +func (x *Generator) GenIndexField() { + x.genIndexField() + x.genOrderedIndexField() +} + +func (x *Generator) GenIndexLoader() { + x.genIndexLoader() + x.genOrderedIndexLoader() +} + +func (x *Generator) GenIndexFinders() { + x.genIndexFinders() + x.genOrderedIndexFinders() +} diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/index.go b/cmd/protoc-gen-go-tableau-loader/indexes/index.go new file mode 100644 index 00000000..c464ff60 --- /dev/null +++ b/cmd/protoc-gen-go-tableau-loader/indexes/index.go @@ -0,0 +1,349 @@ +package indexes + +import ( + "fmt" + "strings" + + "github.com/iancoleman/strcase" + "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" + "github.com/tableauio/loader/internal/index" + "github.com/tableauio/loader/internal/loadutil" + "github.com/tableauio/loader/internal/options" +) + +func (x *Generator) needGenerateIndex() bool { + return options.NeedGenIndex(x.message.Desc, options.LangGO) +} + +func (x *Generator) indexMapType(index *index.LevelIndex) string { + return fmt.Sprintf("%s_Index_%sMap", x.messagerName(), index.Name()) +} + +func (x *Generator) indexMapKeyType(index *index.LevelIndex) string { + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + return helper.ParseGoType(x.gen, x.g, field.FD) + } else { + // multi-column index + return fmt.Sprintf("%s_Index_%sKey", x.messagerName(), index.Name()) + } +} + +func (x *Generator) indexContainerName(index *index.LevelIndex, i int) string { + if i == 0 { + return fmt.Sprintf("index%sMap", strcase.ToCamel(index.Name())) + } + return fmt.Sprintf("index%sMap%d", strcase.ToCamel(index.Name()), i) +} + +func (x *Generator) indexKeys(index *index.LevelIndex) helper.MapKeySlice { + var keys helper.MapKeySlice + for _, field := range index.ColFields { + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseGoType(x.gen, x.g, field.FD), + Name: helper.ParseIndexFieldNameAsFuncParam(x.gen, field.FD), + FieldName: helper.ParseIndexFieldNameAsKeyStructFieldName(x.gen, field.FD), + }) + } + return keys +} + +func (x *Generator) genIndexTypeDef() { + if !x.needGenerateIndex() { + return + } + x.g.P("// Index types.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.g.P("// Index: ", index.Index) + if len(index.ColFields) != 1 { + // multi-column index + keyType := x.indexMapKeyType(index) + keys := x.indexKeys(index) + + // generate key struct + // KeyType must be comparable, refer https://go.dev/blog/maps + x.g.P("type ", keyType, " struct {") + for _, key := range keys { + x.g.P(key.FieldName, " ", key.Type) + } + x.g.P("}") + } + x.g.P("type ", x.indexMapType(index), " = map[", x.indexMapKeyType(index), "][]*", x.mapValueType(index)) + x.g.P() + } + } +} + +func (x *Generator) genIndexField() { + if !x.needGenerateIndex() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.g.P(x.indexContainerName(index, 0), " ", x.indexMapType(index)) + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + if i == 1 { + x.g.P(x.indexContainerName(index, i), " map[", x.keys[0].Type, "]", x.indexMapType(index)) + } else { + levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + x.g.P(x.indexContainerName(index, i), " map[", levelIndexKeyType, "]", x.indexMapType(index)) + } + } + } + } +} + +func (x *Generator) genIndexLoader() { + if !x.needGenerateIndex() { + return + } + defer x.genIndexSorter() + x.g.P("// Index init.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.g.P("x.", x.indexContainerName(index, 0), " = make(", x.indexMapType(index), ")") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + if i == 1 { + x.g.P("x.", x.indexContainerName(index, i), " = make(map[", x.keys[0].Type, "]", x.indexMapType(index), ")") + } else { + levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + x.g.P("x.", x.indexContainerName(index, i), " = make(map[", levelIndexKeyType, "]", x.indexMapType(index), ")") + } + } + } + } + parentDataName := "x.data" + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.genOneIndexLoader(index, levelMessage.MapDepth, parentDataName) + } + keyName := fmt.Sprintf("k%d", levelMessage.MapDepth) + valueName := fmt.Sprintf("v%d", levelMessage.Depth) + if levelMessage.FD == nil { + break + } + if !levelMessage.NextLevel.NeedGenIndex() { + break + } + if levelMessage.FD.IsMap() { + x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + x.g.P("_ = ", keyName) + } else { + x.g.P("for _ , ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + } + parentDataName = valueName + defer x.g.P("}") + } +} + +func (x *Generator) genOneIndexLoader(index *index.LevelIndex, depth int, parentDataName string) { + x.g.P("{") + x.g.P("// Index: ", index.Index) + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take the first field + fieldName, _ := x.parseKeyFieldNameAndSuffix(field) + if field.FD.IsList() { + valueName := fmt.Sprintf("v%d", depth) + x.g.P("for _ , ", valueName, " := range ", parentDataName, fieldName, " {") + x.g.P("key := ", valueName) + x.genIndexLoaderCommon(depth, index, parentDataName) + x.g.P("}") + } else { + x.g.P("key := ", parentDataName, fieldName) + x.genIndexLoaderCommon(depth, index, parentDataName) + } + } else { + // multi-column index + x.generateOneMulticolumnIndex(depth, index, parentDataName, nil) + } + x.g.P("}") +} + +func (x *Generator) generateOneMulticolumnIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { + cursor := len(keys) + if cursor >= len(index.ColFields) { + keyType := x.indexMapKeyType(index) + x.g.P("key := ", keyType, " {", keys.GenGetArguments(), "}") + x.genIndexLoaderCommon(depth, index, parentDataName) + return + } + field := index.ColFields[cursor] + fieldName, _ := x.parseKeyFieldNameAndSuffix(field) + if field.FD.IsList() { + itemName := fmt.Sprintf("indexItem%d", cursor) + x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") + keys = keys.AddMapKey(helper.MapKey{Name: itemName}) + x.generateOneMulticolumnIndex(depth, index, parentDataName, keys) + x.g.P("}") + } else { + key := parentDataName + fieldName + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneMulticolumnIndex(depth, index, parentDataName, keys) + } +} + +func (x *Generator) genIndexLoaderCommon(depth int, index *index.LevelIndex, parentDataName string) { + indexContainerName := x.indexContainerName(index, 0) + x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") + for i := 1; i <= depth-2; i++ { + if i > len(x.keys) { + break + } + indexContainerName := x.indexContainerName(index, i) + if i == 1 { + x.g.P("if x.", indexContainerName, "[k1] == nil {") + x.g.P("x.", indexContainerName, "[k1] = make(", x.indexMapType(index), ")") + x.g.P("}") + x.g.P("x.", indexContainerName, "[k1][key] = append(x.", indexContainerName, "[k1][key], ", parentDataName, ")") + } else { + var fields []string + for j := 1; j <= i; j++ { + fields = append(fields, fmt.Sprintf("k%d", j)) + } + levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + keyName := indexContainerName + "Keys" + x.g.P(keyName, " := ", levelIndexKeyType, "{", strings.Join(fields, ", "), "}") + x.g.P("if x.", indexContainerName, "[", keyName, "] == nil {") + x.g.P("x.", indexContainerName, "[", keyName, "] = make(", x.indexMapType(index), ")") + x.g.P("}") + x.g.P("x.", indexContainerName, "[", keyName, "][key] = append(x.", indexContainerName, "[", keyName, "][key], ", parentDataName, ")") + } + } +} + +func (x *Generator) genIndexSorter() { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + if len(index.SortedColFields) != 0 { + x.g.P("// Index(sort): ", index.Index) + indexContainerName := x.indexContainerName(index, 0) + x.g.P(indexContainerName, "Sorter := func(itemList []*", x.mapValueType(index), ") func(i, j int) bool {") + x.g.P("return func(i, j int) bool {") + for i, field := range index.SortedColFields { + fieldName, _ := x.parseKeyFieldNameAndSuffix(field) + if i == len(index.SortedColFields)-1 { + x.g.P("return itemList[i]", fieldName, " < itemList[j]", fieldName) + } else { + x.g.P("if itemList[i]", fieldName, " != itemList[j]", fieldName, " {") + x.g.P("return itemList[i]", fieldName, " < itemList[j]", fieldName) + x.g.P("}") + } + } + x.g.P("}") + x.g.P("}") + x.g.P("for _, itemList := range x.", x.indexContainerName(index, 0), " {") + x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") + x.g.P("}") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + x.g.P("for _, itemMap := range x.", x.indexContainerName(index, i), " {") + x.g.P("for _, itemList := range itemMap {") + x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") + x.g.P("}") + x.g.P("}") + } + } + } + } +} + +func (x *Generator) genIndexFinders() { + if !x.needGenerateIndex() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + indexContainerName := x.indexContainerName(index, 0) + messagerName := x.messagerName() + x.g.P("// Index: ", index.Index) + x.g.P() + + x.g.P("// Find", index.Name(), "Map finds the index: key(", index.Index, ") to value(", x.mapValueType(index), ") map.") + x.g.P("// One key may correspond to multiple values, which are represented by a slice.") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map() ", x.indexMapType(index), " {") + x.g.P("return x.", indexContainerName) + x.g.P("}") + x.g.P() + + keys := x.indexKeys(index) + params := keys.GenGetParams() + args := keys.GenGetArguments() + x.g.P("// Find", index.Name(), " finds a slice of all values of the given key(s).") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "(", params, ") []*", x.mapValueType(index), " {") + if len(index.ColFields) == 1 { + x.g.P("return x.", indexContainerName, "[", args, "]") + } else { + x.g.P("return x.", indexContainerName, "[", x.indexMapKeyType(index), "{", args, "}]") + } + x.g.P("}") + x.g.P() + + x.g.P("// FindFirst", index.Name(), " finds the first value of the given key(s),") + x.g.P("// or nil if no value found.") + x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), "(", params, ") *", x.mapValueType(index), " {") + x.g.P("val := x.Find", index.Name(), "(", args, ")") + x.g.P("if len(val) > 0 {") + x.g.P("return val[0]") + x.g.P("}") + x.g.P("return nil") + x.g.P("}") + x.g.P() + + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + indexContainerName := x.indexContainerName(index, i) + partKeys := x.keys[:i] + partParams := partKeys.GenGetParams() + partArgs := partKeys.GenGetArguments() + + x.g.P("// Find", index.Name(), "Map", i, " finds the index: key(", index.Index, ") to value(", x.mapValueType(index), "),") + x.g.P("// which is the upper ", loadutil.Ordinal(i), "-level map specified by (", partArgs, ").") + x.g.P("// One key may correspond to multiple values, which are represented by a slice.") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map", i, "(", partParams, ") ", x.indexMapType(index), " {") + if len(partKeys) == 1 { + x.g.P("return x.", indexContainerName, "[", partArgs, "]") + } else { + levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + x.g.P("return x.", indexContainerName, "[", levelIndexKeyType, "{", partArgs, "}]") + } + x.g.P("}") + x.g.P() + + x.g.P("// Find", index.Name(), i, " finds a slice of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map") + x.g.P("// specified by (", partArgs, ").") + x.g.P("func (x *", messagerName, ") Find", index.Name(), i, "(", partParams, ", ", params, ") []*", x.mapValueType(index), " {") + if len(index.ColFields) == 1 { + x.g.P("return x.Find", index.Name(), "Map", i, "(", partArgs, ")[", args, "]") + } else { + x.g.P("return x.Find", index.Name(), "Map", i, "(", partArgs, ")[", x.indexMapKeyType(index), "{", args, "}]") + } + x.g.P("}") + x.g.P() + + x.g.P("// FindFirst", index.Name(), i, " finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map") + x.g.P("// specified by (", partArgs, "), or nil if no value found.") + x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), i, "(", partParams, ", ", params, ") *", x.mapValueType(index), " {") + x.g.P("val := x.Find", index.Name(), i, "(", partArgs, ", ", args, ")") + x.g.P("if len(val) > 0 {") + x.g.P("return val[0]") + x.g.P("}") + x.g.P("return nil") + x.g.P("}") + x.g.P() + } + } + } +} diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/orderedindex.go b/cmd/protoc-gen-go-tableau-loader/indexes/orderedindex.go new file mode 100644 index 00000000..b11fc1d8 --- /dev/null +++ b/cmd/protoc-gen-go-tableau-loader/indexes/orderedindex.go @@ -0,0 +1,382 @@ +package indexes + +import ( + "fmt" + "strings" + + "github.com/iancoleman/strcase" + "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" + "github.com/tableauio/loader/internal/index" + "github.com/tableauio/loader/internal/loadutil" + "github.com/tableauio/loader/internal/options" +) + +func (x *Generator) needGenerateOrderedIndex() bool { + return options.NeedGenOrderedIndex(x.message.Desc, options.LangGO) +} + +func (x *Generator) orderedIndexMapType(index *index.LevelIndex) string { + return fmt.Sprintf("%s_OrderedIndex_%sMap", x.messagerName(), index.Name()) +} + +func (x *Generator) orderedIndexMapKeyType(index *index.LevelIndex) string { + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + return helper.ParseOrderedIndexKeyType(x.gen, x.g, field.FD) + } else { + // multi-column index + return fmt.Sprintf("%s_OrderedIndex_%sKey", x.messagerName(), index.Name()) + } +} + +func (x *Generator) orderedIndexContainerName(index *index.LevelIndex, i int) string { + if i == 0 { + return fmt.Sprintf("orderedIndex%sMap", strcase.ToCamel(index.Name())) + } + return fmt.Sprintf("orderedIndex%sMap%d", strcase.ToCamel(index.Name()), i) +} + +func (x *Generator) mapCtor(index *index.LevelIndex) string { + if len(index.ColFields) == 1 { + // single-column index + return "New" + } else { + // multi-column index + return "New2" + } +} + +func (x *Generator) orderedIndexKeys(index *index.LevelIndex) helper.MapKeySlice { + var keys helper.MapKeySlice + for _, field := range index.ColFields { + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseOrderedIndexKeyType(x.gen, x.g, field.FD), + Name: helper.ParseIndexFieldNameAsFuncParam(x.gen, field.FD), + FieldName: helper.ParseIndexFieldNameAsKeyStructFieldName(x.gen, field.FD), + }) + } + return keys +} + +func (x *Generator) genOrderedIndexTypeDef() { + if !x.needGenerateOrderedIndex() { + return + } + x.g.P("// OrderedIndex types.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.g.P("// OrderedIndex: ", index.Index) + if len(index.ColFields) != 1 { + // multi-column index + keyType := x.orderedIndexMapKeyType(index) + keys := x.orderedIndexKeys(index) + + // generate key struct + x.g.P("type ", keyType, " struct {") + for _, key := range keys { + x.g.P(key.FieldName, " ", key.Type) + } + x.g.P("}") + x.g.P() + + // generate Less func to implement cmp.Ordered interface + x.g.P("func (x ", keyType, ") Less(other ", keyType, ") bool {") + for i, key := range keys { + if i == len(keys)-1 { + x.g.P("return x.", key.FieldName, " < other.", key.FieldName) + } else { + x.g.P("if x.", key.FieldName, " != other.", key.FieldName, " {") + x.g.P("return x.", key.FieldName, " < other.", key.FieldName) + x.g.P("}") + } + } + x.g.P("}") + x.g.P() + } + x.g.P("type ", x.orderedIndexMapType(index), " = ", helper.TreeMapPackage.Ident("TreeMap"), "[", x.orderedIndexMapKeyType(index), ", []*", x.mapValueType(index), "]") + x.g.P() + } + } +} + +func (x *Generator) genOrderedIndexField() { + if !x.needGenerateOrderedIndex() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.g.P(x.orderedIndexContainerName(index, 0), " *", x.orderedIndexMapType(index)) + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + if i == 1 { + x.g.P(x.orderedIndexContainerName(index, i), " map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index)) + } else { + levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + x.g.P(x.orderedIndexContainerName(index, i), " map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index)) + } + } + } + } +} + +func (x *Generator) genOrderedIndexLoader() { + if !x.needGenerateOrderedIndex() { + return + } + defer x.genOrderedIndexSorter() + x.g.P("// OrderedIndex init.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.g.P("x.", x.orderedIndexContainerName(index, 0), " = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.orderedIndexMapKeyType(index), ", []*", x.mapValueType(index), "]()") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + if i == 1 { + x.g.P("x.", x.orderedIndexContainerName(index, i), " = make(map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index), ")") + } else { + levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + x.g.P("x.", x.orderedIndexContainerName(index, i), " = make(map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index), ")") + } + } + } + } + parentDataName := "x.data" + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.genOneOrderedIndexLoader(index, levelMessage.MapDepth, parentDataName) + } + keyName := fmt.Sprintf("k%d", levelMessage.MapDepth) + valueName := fmt.Sprintf("v%d", levelMessage.Depth) + if levelMessage.FD == nil { + break + } + if !levelMessage.NextLevel.NeedGenOrderedIndex() { + break + } + if levelMessage.FD.IsMap() { + x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + x.g.P("_ = ", keyName) + } else { + x.g.P("for _ , ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + } + parentDataName = valueName + defer x.g.P("}") + } +} + +func (x *Generator) genOneOrderedIndexLoader(index *index.LevelIndex, depth int, parentDataName string) { + x.g.P("{") + x.g.P("// OrderedIndex: ", index.Index) + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take the first field + fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) + if field.FD.IsList() { + valueName := fmt.Sprintf("v%d", depth) + x.g.P("for _ , ", valueName, " := range ", parentDataName, fieldName, " {") + x.g.P("key := ", valueName, suffix) + x.genOrderedIndexLoaderCommon(depth, index, parentDataName) + x.g.P("}") + } else { + x.g.P("key := ", parentDataName, fieldName, suffix) + x.genOrderedIndexLoaderCommon(depth, index, parentDataName) + } + } else { + // multi-column index + x.generateOneMulticolumnOrderedIndex(depth, index, parentDataName, nil) + } + x.g.P("}") +} + +func (x *Generator) generateOneMulticolumnOrderedIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { + cursor := len(keys) + if cursor >= len(index.ColFields) { + keyType := x.orderedIndexMapKeyType(index) + x.g.P("key := ", keyType, " {", keys.GenGetArguments(), "}") + x.genOrderedIndexLoaderCommon(depth, index, parentDataName) + return + } + field := index.ColFields[cursor] + fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) + if field.FD.IsList() { + itemName := fmt.Sprintf("indexItem%d", cursor) + x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") + key := itemName + suffix + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneMulticolumnOrderedIndex(depth, index, parentDataName, keys) + x.g.P("}") + } else { + key := parentDataName + fieldName + suffix + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneMulticolumnOrderedIndex(depth, index, parentDataName, keys) + } +} + +func (x *Generator) genOrderedIndexLoaderCommon(depth int, index *index.LevelIndex, parentDataName string) { + indexContainerName := x.orderedIndexContainerName(index, 0) + x.g.P("value, _ := x.", indexContainerName, ".Get(key)") + x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") + for i := 1; i <= depth-2; i++ { + if i > len(x.keys) { + break + } + orderedIndexContainerName := x.orderedIndexContainerName(index, i) + valueName := orderedIndexContainerName + "Value" + if i == 1 { + x.g.P("if x.", orderedIndexContainerName, "[k1] == nil {") + x.g.P("x.", orderedIndexContainerName, "[k1] = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.orderedIndexMapKeyType(index), ", []*", x.mapValueType(index), "]()") + x.g.P("}") + x.g.P(valueName, ", _ := x.", orderedIndexContainerName, "[k1].Get(key)") + x.g.P("x.", orderedIndexContainerName, "[k1].Put(key, append(", valueName, ", ", parentDataName, "))") + } else { + var fields []string + for j := 1; j <= i; j++ { + fields = append(fields, fmt.Sprintf("k%d", j)) + } + levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + keyName := orderedIndexContainerName + "Keys" + x.g.P(keyName, " := ", levelIndexKeyType, "{", strings.Join(fields, ", "), "}") + x.g.P("if x.", orderedIndexContainerName, "[", keyName, "] == nil {") + x.g.P("x.", orderedIndexContainerName, "[", keyName, "] = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.orderedIndexMapKeyType(index), ", []*", x.mapValueType(index), "]()") + x.g.P("}") + x.g.P(valueName, ", _ := x.", orderedIndexContainerName, "[", keyName, "].Get(key)") + x.g.P("x.", orderedIndexContainerName, "[", keyName, "].Put(key, append(", valueName, ", ", parentDataName, "))") + } + } +} + +func (x *Generator) genOrderedIndexSorter() { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + if len(index.SortedColFields) != 0 { + x.g.P("// OrderedIndex(sort): ", index.Index) + indexContainerName := x.orderedIndexContainerName(index, 0) + x.g.P(indexContainerName, "Sorter := func(itemList []*", x.mapValueType(index), ") func(i, j int) bool {") + x.g.P("return func(i, j int) bool {") + for i, field := range index.SortedColFields { + fieldName, _ := x.parseKeyFieldNameAndSuffix(field) + if i == len(index.SortedColFields)-1 { + x.g.P("return itemList[i]", fieldName, " < itemList[j]", fieldName) + } else { + x.g.P("if itemList[i]", fieldName, " != itemList[j]", fieldName, " {") + x.g.P("return itemList[i]", fieldName, " < itemList[j]", fieldName) + x.g.P("}") + } + } + x.g.P("}") + x.g.P("}") + x.g.P("x.", x.orderedIndexContainerName(index, 0), ".Range(func(key ", x.orderedIndexMapKeyType(index), ", itemList []*", x.mapValueType(index), ") bool {") + x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") + x.g.P("return true") + x.g.P("})") + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + x.g.P("for _, itemMap := range x.", x.orderedIndexContainerName(index, i), " {") + x.g.P("itemMap.Range(func(key ", x.orderedIndexMapKeyType(index), ", itemList []*", x.mapValueType(index), ") bool {") + x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") + x.g.P("return true") + x.g.P("})") + x.g.P("}") + } + } + } + } +} + +func (x *Generator) genOrderedIndexFinders() { + if !x.needGenerateOrderedIndex() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + indexContainerName := x.orderedIndexContainerName(index, 0) + messagerName := x.messagerName() + x.g.P("// OrderedIndex: ", index.Index) + x.g.P() + + x.g.P("// Find", index.Name(), "Map finds the ordered index: key(", index.Index, ") to value(", x.mapValueType(index), ") treemap.") + x.g.P("// One key may correspond to multiple values, which are represented by a slice.") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map() *", x.orderedIndexMapType(index), " {") + x.g.P("return x.", indexContainerName) + x.g.P("}") + x.g.P() + + keys := x.orderedIndexKeys(index) + params := keys.GenGetParams() + args := keys.GenGetArguments() + x.g.P("// Find", index.Name(), " finds a slice of all values of the given key(s).") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "(", params, ") []*", x.mapValueType(index), " {") + if len(index.ColFields) == 1 { + x.g.P("val, _ := x.", indexContainerName, ".Get(", args, ")") + } else { + x.g.P("val, _ := x.", indexContainerName, ".Get(", x.orderedIndexMapKeyType(index), "{", args, "})") + } + x.g.P("return val") + x.g.P("}") + x.g.P() + + x.g.P("// FindFirst", index.Name(), " finds the first value of the given key(s),") + x.g.P("// or nil if no value found.") + x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), "(", params, ") *", x.mapValueType(index), " {") + x.g.P("val := x.Find", index.Name(), "(", args, ")") + x.g.P("if len(val) > 0 {") + x.g.P("return val[0]") + x.g.P("}") + x.g.P("return nil") + x.g.P("}") + x.g.P() + + for i := 1; i <= levelMessage.MapDepth-2; i++ { + if i > len(x.keys) { + break + } + orderedIndexContainerName := x.orderedIndexContainerName(index, i) + partKeys := x.keys[:i] + partParams := partKeys.GenGetParams() + partArgs := partKeys.GenGetArguments() + + x.g.P("// Find", index.Name(), "Map", i, " finds the index: key(", index.Index, ") to value(", x.mapValueType(index), "),") + x.g.P("// which is the upper ", loadutil.Ordinal(i), "-level treemap specified by (", partArgs, ").") + x.g.P("// One key may correspond to multiple values, which are represented by a slice.") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map", i, "(", partParams, ") *", x.orderedIndexMapType(index), " {") + if len(partKeys) == 1 { + x.g.P("return x.", orderedIndexContainerName, "[", partArgs, "]") + } else { + levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + x.g.P("return x.", orderedIndexContainerName, "[", levelIndexKeyType, "{", partArgs, "}]") + } + x.g.P("}") + x.g.P() + + x.g.P("// Find", index.Name(), i, " finds a slice of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level treemap") + x.g.P("// specified by (", partArgs, ").") + x.g.P("func (x *", messagerName, ") Find", index.Name(), i, "(", partParams, ", ", params, ") []*", x.mapValueType(index), " {") + if len(index.ColFields) == 1 { + x.g.P("val, _ := x.Find", index.Name(), "Map", i, "(", partArgs, ").Get(", args, ")") + } else { + x.g.P("val, _ := x.Find", index.Name(), "Map", i, "(", partArgs, ").Get(", x.orderedIndexMapKeyType(index), "{", args, "})") + } + x.g.P("return val") + x.g.P("}") + x.g.P() + + x.g.P("// FindFirst", index.Name(), i, " finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level treemap") + x.g.P("// specified by (", partArgs, "), or nil if no value found.") + x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), i, "(", partParams, ", ", params, ") *", x.mapValueType(index), " {") + x.g.P("val := x.Find", index.Name(), i, "(", partArgs, ", ", args, ")") + x.g.P("if len(val) > 0 {") + x.g.P("return val[0]") + x.g.P("}") + x.g.P("return nil") + x.g.P("}") + x.g.P() + } + } + } +} diff --git a/cmd/protoc-gen-go-tableau-loader/messager.go b/cmd/protoc-gen-go-tableau-loader/messager.go index a4f32cbf..eab4859c 100644 --- a/cmd/protoc-gen-go-tableau-loader/messager.go +++ b/cmd/protoc-gen-go-tableau-loader/messager.go @@ -4,12 +4,11 @@ import ( "fmt" "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" - idx "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/index" - "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/orderedindex" + "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/indexes" "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/orderedmap" "github.com/tableauio/loader/internal/extensions" "github.com/tableauio/loader/internal/index" - "github.com/tableauio/loader/internal/options" + "github.com/tableauio/loader/internal/loadutil" "github.com/tableauio/tableau/proto/tableaupb" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/proto" @@ -66,13 +65,11 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog indexDescriptor := index.ParseIndexDescriptor(message.Desc) orderedMapGenerator := orderedmap.NewGenerator(gen, g, message) - indexGenerator := idx.NewGenerator(gen, g, indexDescriptor, message) - orderedIndexGenerator := orderedindex.NewGenerator(gen, g, indexDescriptor, message) + indexGenerator := indexes.NewGenerator(gen, g, indexDescriptor, message) // type definitions orderedMapGenerator.GenOrderedMapTypeDef() indexGenerator.GenIndexTypeDef() - orderedIndexGenerator.GenOrderedIndexTypeDef() g.P("// ", messagerName, " is a wrapper around protobuf message: ", message.GoIdent, ".") g.P("//") @@ -87,7 +84,6 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog g.P("data, originalData *", message.GoIdent) orderedMapGenerator.GenOrderedMapField() indexGenerator.GenIndexField() - orderedIndexGenerator.GenOrderedIndexField() g.P("}") g.P() @@ -107,7 +103,7 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog g.P("}") g.P() - g.P("// Load fills ", messagerName, "'s inner message from file in the specified directory and format.") + g.P("// Load loads ", messagerName, "'s content in the given dir, based on format and messager options.") g.P("func (x *", messagerName, ") Load(dir string, format ", helper.FormatPackage.Ident("Format"), " , opts *", helper.LoadPackage.Ident("MessagerOptions"), ") error {") g.P("start := ", helper.TimePackage.Ident("Now"), "()") g.P("defer func () {") @@ -125,7 +121,7 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog g.P("}") g.P() - g.P("// Store writes ", messagerName, "'s inner message to file in the specified directory and format.") + g.P("// Store stores ", messagerName, "'s content to file in the specified directory and format.") g.P("// Available formats: JSON, Bin, and Text.") g.P("func (x *", messagerName, ") Store(dir string, format ", helper.FormatPackage.Ident("Format"), " , options ...", helper.StorePackage.Ident("Option"), ") error {") g.P("return ", helper.StorePackage.Ident("Store"), "(x.Data(), dir, format, options...)") @@ -153,12 +149,11 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog g.P("}") g.P() - if options.NeedGenOrderedMap(message.Desc, options.LangGO) || options.NeedGenIndex(message.Desc, options.LangGO) || options.NeedGenOrderedIndex(message.Desc, options.LangGO) { + if orderedMapGenerator.NeedGenerate() || indexGenerator.NeedGenerate() { g.P("// processAfterLoad runs after this messager is loaded.") g.P("func (x *", messagerName, ") processAfterLoad() error {") orderedMapGenerator.GenOrderedMapLoader() indexGenerator.GenIndexLoader() - orderedIndexGenerator.GenOrderedIndexLoader() g.P("return nil") g.P("}") g.P() @@ -168,19 +163,18 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog genMapGetters(gen, g, message, 1, nil, messagerName) orderedMapGenerator.GenOrderedMapGetters() indexGenerator.GenIndexFinders() - orderedIndexGenerator.GenOrderedIndexFinders() } -func genMapGetters(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protogen.Message, depth int, keys helper.MapKeys, messagerName string) { +func genMapGetters(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protogen.Message, depth int, keys helper.MapKeySlice, messagerName string) { for _, field := range message.Fields { fd := field.Desc if fd.IsMap() { keys = keys.AddMapKey(helper.MapKey{ Type: helper.ParseMapKeyType(fd.MapKey()), - Name: helper.ParseMapFieldName(fd), + Name: helper.ParseMapFieldNameAsFuncParam(fd), }) getter := fmt.Sprintf("Get%v", depth) - g.P("// ", getter, " finds value in the ", depth, "-level map. It will return") + g.P("// ", getter, " finds value in the ", loadutil.Ordinal(depth), "-level map. It will return") g.P("// NotFound error if the key is not found.") g.P("func (x *", messagerName, ") ", getter, "(", keys.GenGetParams(), ") (", helper.ParseMapValueType(gen, g, fd), ", error) {") diff --git a/cmd/protoc-gen-go-tableau-loader/orderedindex/ordered_index.go b/cmd/protoc-gen-go-tableau-loader/orderedindex/ordered_index.go deleted file mode 100644 index 96f619e0..00000000 --- a/cmd/protoc-gen-go-tableau-loader/orderedindex/ordered_index.go +++ /dev/null @@ -1,309 +0,0 @@ -package orderedindex - -import ( - "fmt" - - "github.com/iancoleman/strcase" - "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" - "github.com/tableauio/loader/internal/index" - "github.com/tableauio/loader/internal/options" - "google.golang.org/protobuf/compiler/protogen" - "google.golang.org/protobuf/reflect/protoreflect" -) - -type Generator struct { - gen *protogen.Plugin - g *protogen.GeneratedFile - descriptor *index.IndexDescriptor - message *protogen.Message -} - -func NewGenerator(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { - return &Generator{ - gen: gen, - g: g, - descriptor: descriptor, - message: message, - } -} - -func (x *Generator) needGenerate() bool { - return options.NeedGenOrderedIndex(x.message.Desc, options.LangGO) -} - -func (x *Generator) messagerName() string { - return string(x.message.Desc.Name()) -} - -func (x *Generator) mapType(index *index.LevelIndex) string { - return fmt.Sprintf("%s_OrderedIndex_%sMap", x.messagerName(), index.Name()) -} - -func (x *Generator) mapKeyType(index *index.LevelIndex) string { - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take first field - return helper.ParseOrderedIndexKeyType(x.gen, x.g, field.FD) - } else { - // multi-column index - return fmt.Sprintf("%s_OrderedIndex_%sKey", x.messagerName(), index.Name()) - } -} - -func (x *Generator) mapValueType(index *index.LevelIndex) protogen.GoIdent { - return helper.FindMessageGoIdent(x.gen, index.MD) -} - -func (x *Generator) indexContainerName(index *index.LevelIndex) string { - return fmt.Sprintf("orderedIndex%sMap", strcase.ToCamel(index.Name())) -} - -func (x *Generator) mapCtor(index *index.LevelIndex) string { - if len(index.ColFields) == 1 { - // single-column index - return "New" - } else { - // multi-column index - return "New2" - } -} - -func (x *Generator) indexKeys(index *index.LevelIndex) helper.MapKeys { - var keys helper.MapKeys - for _, field := range index.ColFields { - keys = keys.AddMapKey(helper.MapKey{ - Type: helper.ParseOrderedIndexKeyType(x.gen, x.g, field.FD), - Name: helper.ParseIndexFieldNameAsFuncParam(x.gen, field.FD), - FieldName: helper.ParseIndexFieldNameAsKeyStructFieldName(x.gen, field.FD), - }) - } - return keys -} - -func (x *Generator) fieldGetter(fd protoreflect.FieldDescriptor) string { - return fmt.Sprintf(".Get%s()", helper.ParseIndexFieldName(x.gen, fd)) -} - -func (x *Generator) parseKeyFieldNameAndSuffix(field *index.LevelField) (string, string) { - var fieldName, suffix string - for i, leveledFd := range field.LeveledFDList { - fieldName += x.fieldGetter(leveledFd) - if i == len(field.LeveledFDList)-1 && leveledFd.Message() != nil { - switch leveledFd.Message().FullName() { - case "google.protobuf.Timestamp", "google.protobuf.Duration": - suffix = ".GetSeconds()" - default: - } - } - } - return fieldName, suffix -} - -func (x *Generator) GenOrderedIndexTypeDef() { - if !x.needGenerate() { - return - } - x.g.P("// OrderedIndex types.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - x.g.P("// OrderedIndex: ", index.Index) - if len(index.ColFields) != 1 { - // multi-column index - keyType := x.mapKeyType(index) - keys := x.indexKeys(index) - - // generate key struct - x.g.P("type ", keyType, " struct {") - for _, key := range keys { - x.g.P(key.FieldName, " ", key.Type) - } - x.g.P("}") - x.g.P() - - // generate Less func to implement cmp.Ordered interface - x.g.P("func (x ", keyType, ") Less(other ", keyType, ") bool {") - for i, key := range keys { - if i == len(keys)-1 { - x.g.P("return x.", key.FieldName, " < other.", key.FieldName) - } else { - x.g.P("if x.", key.FieldName, " != other.", key.FieldName, " {") - x.g.P("return x.", key.FieldName, " < other.", key.FieldName) - x.g.P("}") - } - } - x.g.P("}") - x.g.P() - } - x.g.P("type ", x.mapType(index), " = ", helper.TreeMapPackage.Ident("TreeMap"), "[", x.mapKeyType(index), ", []*", x.mapValueType(index), "]") - x.g.P() - } - } -} - -func (x *Generator) GenOrderedIndexField() { - if !x.needGenerate() { - return - } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - x.g.P(x.indexContainerName(index), " *", x.mapType(index)) - } - } -} - -func (x *Generator) GenOrderedIndexLoader() { - if !x.needGenerate() { - return - } - x.g.P("// OrderedIndex init.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - x.g.P("x.", x.indexContainerName(index), " = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.mapKeyType(index), ", []*", x.mapValueType(index), "]()") - } - } - parentDataName := "x.data" - depth := 1 - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - x.genOneOrderedIndexLoader(index, depth, parentDataName) - } - itemName := fmt.Sprintf("item%d", depth) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGen() { - break - } - x.g.P("for _, ", itemName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") - parentDataName = itemName - depth++ - } - for i := depth - 1; i > 0; i-- { - x.g.P("}") - } - x.genOrderedIndexSorter() -} - -func (x *Generator) genOneOrderedIndexLoader(index *index.LevelIndex, depth int, parentDataName string) { - x.g.P("{") - x.g.P("// OrderedIndex: ", index.Index) - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take the first field - fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) - indexContainerName := x.indexContainerName(index) - if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) - x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") - x.g.P("key := ", itemName, suffix) - x.g.P("value, _ := x.", indexContainerName, ".Get(key)") - x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") - x.g.P("}") - } else { - x.g.P("key := ", parentDataName, fieldName, suffix) - x.g.P("value, _ := x.", indexContainerName, ".Get(key)") - x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") - } - } else { - // multi-column index - x.generateOneMulticolumnOrderedIndex(depth, index, parentDataName, nil) - } - x.g.P("}") -} - -func (x *Generator) generateOneMulticolumnOrderedIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeys) { - cursor := len(keys) - if cursor >= len(index.ColFields) { - keyType := x.mapKeyType(index) - indexContainerName := x.indexContainerName(index) - x.g.P("key := ", keyType, " {", keys.GenGetArguments(), "}") - x.g.P("value, _ := x.", indexContainerName, ".Get(key)") - x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") - return - } - field := index.ColFields[cursor] - fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) - if field.FD.IsList() { - itemName := fmt.Sprintf("indexItem%d", cursor) - x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") - key := itemName + suffix - keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneMulticolumnOrderedIndex(depth+1, index, parentDataName, keys) - x.g.P("}") - } else { - key := parentDataName + fieldName + suffix - keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneMulticolumnOrderedIndex(depth, index, parentDataName, keys) - } -} - -func (x *Generator) genOrderedIndexSorter() { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - if len(index.SortedColFields) != 0 { - x.g.P("// OrderedIndex(sort): ", index.Index) - x.g.P("x.", x.indexContainerName(index), ".Range(func(key ", x.mapKeyType(index), ", item []*", x.mapValueType(index), ") bool {") - x.g.P(helper.SortPackage.Ident("Slice"), "(item, func(i, j int) bool {") - for i, field := range index.SortedColFields { - fieldName, _ := x.parseKeyFieldNameAndSuffix(field) - if i == len(index.SortedColFields)-1 { - x.g.P("return item[i]", fieldName, " < item[j]", fieldName) - } else { - x.g.P("if item[i]", fieldName, " != item[j]", fieldName, " {") - x.g.P("return item[i]", fieldName, " < item[j]", fieldName) - x.g.P("}") - } - } - x.g.P("})") - x.g.P("return true") - x.g.P("})") - } - } - } -} - -func (x *Generator) GenOrderedIndexFinders() { - if !x.needGenerate() { - return - } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - indexContainerName := x.indexContainerName(index) - messagerName := x.messagerName() - x.g.P("// OrderedIndex: ", index.Index) - x.g.P() - - x.g.P("// Find", index.Name(), "Map finds the ordered index (", index.Index, ") to value (", x.mapValueType(index), ") treemap.") - x.g.P("// One key may correspond to multiple values, which are contained by a slice.") - x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map() *", x.mapType(index), " {") - x.g.P("return x.", indexContainerName) - x.g.P("}") - x.g.P() - - keys := x.indexKeys(index) - params := keys.GenGetParams() - args := keys.GenGetArguments() - x.g.P("// Find", index.Name(), " finds a slice of all values of the given key.") - x.g.P("func (x *", messagerName, ") Find", index.Name(), "(", params, ") []*", x.mapValueType(index), " {") - if len(index.ColFields) == 1 { - x.g.P("val, _ := x.", indexContainerName, ".Get(", args, ")") - } else { - x.g.P("val, _ := x.", indexContainerName, ".Get(", x.mapKeyType(index), "{", args, "})") - } - x.g.P("return val") - x.g.P("}") - x.g.P() - - x.g.P("// FindFirst", index.Name(), " finds the first value of the given key,") - x.g.P("// or nil if no value found.") - x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), "(", params, ") *", x.mapValueType(index), " {") - x.g.P("val := x.Find", index.Name(), "(", args, ")") - x.g.P("if len(val) > 0 {") - x.g.P("return val[0]") - x.g.P("}") - x.g.P("return nil") - x.g.P("}") - x.g.P() - } - } -} diff --git a/cmd/protoc-gen-go-tableau-loader/orderedmap/ordered_map.go b/cmd/protoc-gen-go-tableau-loader/orderedmap/ordered_map.go index f671ce70..39430d7c 100644 --- a/cmd/protoc-gen-go-tableau-loader/orderedmap/ordered_map.go +++ b/cmd/protoc-gen-go-tableau-loader/orderedmap/ordered_map.go @@ -2,9 +2,9 @@ package orderedmap import ( "fmt" - "strings" "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" + "github.com/tableauio/loader/internal/loadutil" "github.com/tableauio/loader/internal/options" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/reflect/protoreflect" @@ -24,7 +24,7 @@ func NewGenerator(gen *protogen.Plugin, g *protogen.GeneratedFile, message *prot } } -func (x *Generator) needGenerate() bool { +func (x *Generator) NeedGenerate() bool { return options.NeedGenOrderedMap(x.message.Desc, options.LangGO) } @@ -32,20 +32,12 @@ func (x *Generator) messagerName() string { return string(x.message.Desc.Name()) } -func (x *Generator) orderedMapPrefix(mapFd protoreflect.FieldDescriptor) string { - if mapFd.MapValue().Kind() == protoreflect.MessageKind { - localMsgProtoName := strings.TrimPrefix(string(mapFd.MapValue().Message().FullName()), string(x.message.Desc.FullName())+".") - return strings.ReplaceAll(localMsgProtoName, ".", "_") - } - return mapFd.MapValue().Kind().String() -} - func (x *Generator) mapType(mapFd protoreflect.FieldDescriptor) string { - return fmt.Sprintf("%s_OrderedMap_%sMap", x.messagerName(), x.orderedMapPrefix(mapFd)) + return fmt.Sprintf("%s_OrderedMap_%sMap", x.messagerName(), helper.ParseLeveledMapPrefix(x.message.Desc, mapFd)) } func (x *Generator) mapValueType(mapFd protoreflect.FieldDescriptor) string { - return fmt.Sprintf("%s_OrderedMap_%sValue", x.messagerName(), x.orderedMapPrefix(mapFd)) + return fmt.Sprintf("%s_OrderedMap_%sValue", x.messagerName(), helper.ParseLeveledMapPrefix(x.message.Desc, mapFd)) } func (x *Generator) mapValueFieldType(fd protoreflect.FieldDescriptor) string { @@ -57,20 +49,20 @@ func (x *Generator) mapValueFieldType(fd protoreflect.FieldDescriptor) string { } func (x *Generator) GenOrderedMapTypeDef() { - if !x.needGenerate() { + if !x.NeedGenerate() { return } x.g.P("// OrderedMap types.") x.genOrderedMapTypeDef(x.message.Desc, 1, nil) } -func (x *Generator) genOrderedMapTypeDef(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys) { +func (x *Generator) genOrderedMapTypeDef(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeySlice) { for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) if fd.IsMap() { nextKeys := keys.AddMapKey(helper.MapKey{ Type: helper.ParseMapKeyType(fd.MapKey()), - Name: helper.ParseMapFieldName(fd), + Name: helper.ParseMapFieldNameAsFuncParam(fd), }) keyType := nextKeys[len(nextKeys)-1].Type if keyType == "bool" { @@ -95,7 +87,7 @@ func (x *Generator) genOrderedMapTypeDef(md protoreflect.MessageDescriptor, dept } func (x *Generator) GenOrderedMapField() { - if !x.needGenerate() { + if !x.NeedGenerate() { return } md := x.message.Desc @@ -109,14 +101,14 @@ func (x *Generator) GenOrderedMapField() { } func (x *Generator) GenOrderedMapLoader() { - if !x.needGenerate() { + if !x.NeedGenerate() { return } x.g.P("// OrderedMap init.") x.genOrderedMapLoader(x.message.Desc, 1, nil, "") } -func (x *Generator) genOrderedMapLoader(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys, lastOrderedMapValue string) { +func (x *Generator) genOrderedMapLoader(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeySlice, lastOrderedMapValue string) { message := helper.FindMessage(x.gen, md) for _, field := range message.Fields { fd := field.Desc @@ -124,7 +116,7 @@ func (x *Generator) genOrderedMapLoader(md protoreflect.MessageDescriptor, depth needConvertBool := len(keys) > 0 && keys[len(keys)-1].Type == "int" nextKeys := keys.AddMapKey(helper.MapKey{ Type: helper.ParseMapKeyType(fd.MapKey()), - Name: helper.ParseMapFieldName(fd), + Name: helper.ParseMapFieldNameAsFuncParam(fd), }) keyType := nextKeys[len(nextKeys)-1].Type needConvertBoolNext := keyType == "bool" @@ -171,13 +163,13 @@ func (x *Generator) genOrderedMapLoader(md protoreflect.MessageDescriptor, depth } func (x *Generator) GenOrderedMapGetters() { - if !x.needGenerate() { + if !x.NeedGenerate() { return } x.genOrderedMapGetters(x.message.Desc, 1, nil) } -func (x *Generator) genOrderedMapGetters(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys) { +func (x *Generator) genOrderedMapGetters(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeySlice) { genGetterName := func(depth int) string { getter := "GetOrderedMap" if depth > 1 { @@ -191,11 +183,11 @@ func (x *Generator) genOrderedMapGetters(md protoreflect.MessageDescriptor, dept getter := genGetterName(depth) orderedMap := x.mapType(fd) if depth == 1 { - x.g.P("// ", getter, " returns the 1-level ordered map.") + x.g.P("// ", getter, " returns the ", loadutil.Ordinal(depth), "-level ordered map.") x.g.P("func (x *", x.messagerName(), ") ", getter, "(", keys.GenGetParams(), ") *", orderedMap, "{") x.g.P("return x.orderedMap ") } else { - x.g.P("// ", getter, " finds value in the ", depth-1, "-level ordered map. It will return") + x.g.P("// ", getter, " finds value in the ", loadutil.Ordinal(depth-1), "-level ordered map. It will return") x.g.P("// NotFound error if the key is not found.") x.g.P("func (x *", x.messagerName(), ") ", getter, "(", keys.GenGetParams(), ") (*", orderedMap, ", error) {") if depth == 2 { @@ -226,7 +218,7 @@ func (x *Generator) genOrderedMapGetters(md protoreflect.MessageDescriptor, dept nextKeys := keys.AddMapKey(helper.MapKey{ Type: helper.ParseMapKeyType(fd.MapKey()), - Name: helper.ParseMapFieldName(fd), + Name: helper.ParseMapFieldNameAsFuncParam(fd), }) if fd.MapValue().Kind() == protoreflect.MessageKind { x.genOrderedMapGetters(fd.MapValue().Message(), depth+1, nextKeys) diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index d5e808d6..e68308ec 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -52,13 +52,23 @@ type LevelMessage struct { // Current level message's all index fields Indexes, OrderedIndexes []*LevelIndex + + // depth of message hierarchy + Depth, MapDepth int } -func (l *LevelMessage) NeedGen() bool { +func (l *LevelMessage) NeedGenIndex() bool { if l == nil { return false } - return len(l.Indexes) != 0 || len(l.OrderedIndexes) != 0 || l.NextLevel.NeedGen() + return len(l.Indexes) != 0 || l.NextLevel.NeedGenIndex() +} + +func (l *LevelMessage) NeedGenOrderedIndex() bool { + if l == nil { + return false + } + return len(l.OrderedIndexes) != 0 || l.NextLevel.NeedGenOrderedIndex() } type LevelIndex struct { @@ -77,21 +87,24 @@ func (l *LevelIndex) Name() string { return name } -func parseLevelMessage(md protoreflect.MessageDescriptor) *LevelMessage { - levelInfo := &LevelMessage{} +func parseLevelMessage(md protoreflect.MessageDescriptor, depth, mapDepth int) *LevelMessage { + levelMsg := &LevelMessage{ + Depth: depth, + MapDepth: mapDepth, + } for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) if fd.IsMap() && fd.MapValue().Kind() == protoreflect.MessageKind { - levelInfo.NextLevel = parseLevelMessage(fd.MapValue().Message()) - levelInfo.FD = fd - return levelInfo + levelMsg.NextLevel = parseLevelMessage(fd.MapValue().Message(), depth+1, mapDepth+1) + levelMsg.FD = fd + return levelMsg } else if fd.IsList() && fd.Kind() == protoreflect.MessageKind { - levelInfo.NextLevel = parseLevelMessage(fd.Message()) - levelInfo.FD = fd - return levelInfo + levelMsg.NextLevel = parseLevelMessage(fd.Message(), depth+1, mapDepth) + levelMsg.FD = fd + return levelMsg } } - return &LevelMessage{} + return levelMsg } // parseRecursively parses multi-column index related info. @@ -169,7 +182,7 @@ func parseCols(cols []string, prefix string, md protoreflect.MessageDescriptor, func ParseIndexDescriptor(md protoreflect.MessageDescriptor) *IndexDescriptor { descriptor := &IndexDescriptor{ - LevelMessage: parseLevelMessage(md), + LevelMessage: parseLevelMessage(md, 1, 1), } indexes, orderedIndexes := ParseWSOptionIndex(md) // parse indexes into level message diff --git a/internal/index/descriptor_test.go b/internal/index/descriptor_test.go index d07423bf..aa5e0e29 100644 --- a/internal/index/descriptor_test.go +++ b/internal/index/descriptor_test.go @@ -34,8 +34,12 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - FD: fd[*protoconf.ItemConf]("item_map"), + Depth: 1, + MapDepth: 1, + FD: fd[*protoconf.ItemConf]("item_map"), NextLevel: &LevelMessage{ + Depth: 2, + MapDepth: 2, Indexes: []*LevelIndex{ { Index: &Index{ @@ -242,6 +246,53 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, }, + OrderedIndexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"ExtType"}, + Name: "ExtType", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("ext_type_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("ext_type_list"), + }, + }, + }, + }, + { + Index: &Index{ + Cols: []string{"Param", "ExtType"}, + SortedCols: []string{"ID"}, + Name: "ParamExtType", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("param_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("param_list"), + }, + }, + { + FD: fd[*protoconf.ItemConf_Item]("ext_type_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("ext_type_list"), + }, + }, + }, + SortedColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("id"), + }, + }, + }, + }, + }, }, }, }, @@ -253,10 +304,16 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - FD: fd[*protoconf.HeroConf]("hero_map"), + Depth: 1, + MapDepth: 1, + FD: fd[*protoconf.HeroConf]("hero_map"), NextLevel: &LevelMessage{ - FD: fd[*protoconf.HeroConf_Hero]("attr_map"), + Depth: 2, + MapDepth: 2, + FD: fd[*protoconf.HeroConf_Hero]("attr_map"), NextLevel: &LevelMessage{ + Depth: 3, + MapDepth: 3, Indexes: []*LevelIndex{ { Index: &Index{ @@ -286,9 +343,13 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - FD: fd[*protoconf.ActivityConf]("activity_map"), + Depth: 1, + MapDepth: 1, + FD: fd[*protoconf.ActivityConf]("activity_map"), NextLevel: &LevelMessage{ - FD: fd[*protoconf.ActivityConf_Activity]("chapter_map"), + Depth: 2, + MapDepth: 2, + FD: fd[*protoconf.ActivityConf_Activity]("chapter_map"), Indexes: []*LevelIndex{ { Index: &Index{ @@ -307,7 +368,9 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, NextLevel: &LevelMessage{ - FD: fd[*protoconf.ActivityConf_Activity_Chapter]("section_map"), + Depth: 3, + MapDepth: 3, + FD: fd[*protoconf.ActivityConf_Activity_Chapter]("section_map"), Indexes: []*LevelIndex{ { Index: &Index{ @@ -350,9 +413,13 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, NextLevel: &LevelMessage{ - FD: fd[*protoconf.Section]("section_item_list"), + Depth: 4, + MapDepth: 4, + FD: fd[*protoconf.Section]("section_item_list"), NextLevel: &LevelMessage{ - FD: fd[*protoconf.Section_SectionItem]("decompose_item_list"), + Depth: 5, + MapDepth: 4, + FD: fd[*protoconf.Section_SectionItem]("decompose_item_list"), Indexes: []*LevelIndex{ { Index: &Index{ @@ -370,7 +437,10 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, }, - NextLevel: &LevelMessage{}, + NextLevel: &LevelMessage{ + Depth: 6, + MapDepth: 4, + }, }, }, }, @@ -385,8 +455,12 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - FD: fd[*protoconf.TaskConf]("task_map"), + Depth: 1, + MapDepth: 1, + FD: fd[*protoconf.TaskConf]("task_map"), NextLevel: &LevelMessage{ + Depth: 2, + MapDepth: 2, Indexes: []*LevelIndex{ { Index: &Index{ @@ -489,6 +563,27 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, }, + { + Index: &Index{ + Cols: []string{"Expiry", "ActivityID"}, + Name: "ActivityExpiry", + }, + MD: md[*protoconf.TaskConf_Task](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.TaskConf_Task]("expiry"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("expiry"), + }, + }, + { + FD: fd[*protoconf.TaskConf_Task]("activity_id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("activity_id"), + }, + }, + }, + }, }, }, }, diff --git a/internal/loadutil/ordinal.go b/internal/loadutil/ordinal.go new file mode 100644 index 00000000..3e004e58 --- /dev/null +++ b/internal/loadutil/ordinal.go @@ -0,0 +1,27 @@ +package loadutil + +import "fmt" + +// Ordinal returns the ordinal representation of a number. +func Ordinal(n int) string { + abs := n + if abs < 0 { + abs = -abs + } + + // handle 11, 12, 13 etc. + if abs%100 >= 11 && abs%100 <= 13 { + return fmt.Sprintf("%dth", n) + } + + switch abs % 10 { + case 1: + return fmt.Sprintf("%dst", n) + case 2: + return fmt.Sprintf("%dnd", n) + case 3: + return fmt.Sprintf("%drd", n) + default: + return fmt.Sprintf("%dth", n) + } +} diff --git a/internal/loadutil/ordinal_test.go b/internal/loadutil/ordinal_test.go new file mode 100644 index 00000000..2831e0bd --- /dev/null +++ b/internal/loadutil/ordinal_test.go @@ -0,0 +1,45 @@ +package loadutil + +import ( + "fmt" + "testing" +) + +func TestOrdinal(t *testing.T) { + tests := []struct { + in int + want string + }{ + {0, "0th"}, + {1, "1st"}, + {2, "2nd"}, + {3, "3rd"}, + {4, "4th"}, + {10, "10th"}, + {11, "11th"}, + {12, "12th"}, + {13, "13th"}, + {21, "21st"}, + {22, "22nd"}, + {23, "23rd"}, + {101, "101st"}, + {111, "111th"}, + {-1, "-1st"}, + {-12, "-12th"}, + {1001, "1001st"}, + {1112, "1112th"}, // 12 -> th + {1123, "1123rd"}, + {214, "214th"}, // 14 -> th + } + + for _, tc := range tests { + tc := tc // capture + t.Run(fmt.Sprintf("%d", tc.in), func(t *testing.T) { + t.Parallel() + got := Ordinal(tc.in) + if got != tc.want { + t.Fatalf("ordinal(%d) = %q; want %q", tc.in, got, tc.want) + } + }) + } +} diff --git a/test/cpp-tableau-loader/src/protoconf/hub.pc.h b/test/cpp-tableau-loader/src/protoconf/hub.pc.h index 618c4681..fb85329d 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/hub.pc.h @@ -126,6 +126,18 @@ class HeroConf; template <> const std::shared_ptr Hub::Get() const; +class Fruit2Conf; +template <> +const std::shared_ptr Hub::Get() const; + +class Fruit3Conf; +template <> +const std::shared_ptr Hub::Get() const; + +class FruitConf; +template <> +const std::shared_ptr Hub::Get() const; + class ItemConf; template <> const std::shared_ptr Hub::Get() const; @@ -174,6 +186,9 @@ class MessagerContainer { std::time_t last_loaded_time_; std::shared_ptr hero_base_conf_; std::shared_ptr hero_conf_; + std::shared_ptr fruit_2_conf_; + std::shared_ptr fruit_3_conf_; + std::shared_ptr fruit_conf_; std::shared_ptr item_conf_; std::shared_ptr patch_merge_conf_; std::shared_ptr patch_replace_conf_; diff --git a/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc b/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc index a5d39608..f9c04280 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc @@ -6,6 +6,7 @@ #include "hub.pc.h" #include "hero_conf.pc.h" +#include "index_conf.pc.h" #include "item_conf.pc.h" namespace tableau { @@ -19,6 +20,21 @@ const std::shared_ptr Hub::Get() const { return GetMessagerContainerWithProvider()->hero_conf_; } +template <> +const std::shared_ptr Hub::Get() const { + return GetMessagerContainerWithProvider()->fruit_2_conf_; +} + +template <> +const std::shared_ptr Hub::Get() const { + return GetMessagerContainerWithProvider()->fruit_3_conf_; +} + +template <> +const std::shared_ptr Hub::Get() const { + return GetMessagerContainerWithProvider()->fruit_conf_; +} + template <> const std::shared_ptr Hub::Get() const { return GetMessagerContainerWithProvider()->item_conf_; @@ -27,12 +43,18 @@ const std::shared_ptr Hub::Get() const { void MessagerContainer::InitShard0() { hero_base_conf_ = std::dynamic_pointer_cast(GetMessager(HeroBaseConf::Name())); hero_conf_ = std::dynamic_pointer_cast(GetMessager(HeroConf::Name())); + fruit_2_conf_ = std::dynamic_pointer_cast(GetMessager(Fruit2Conf::Name())); + fruit_3_conf_ = std::dynamic_pointer_cast(GetMessager(Fruit3Conf::Name())); + fruit_conf_ = std::dynamic_pointer_cast(GetMessager(FruitConf::Name())); item_conf_ = std::dynamic_pointer_cast(GetMessager(ItemConf::Name())); } void Registry::InitShard0() { Register(); Register(); + Register(); + Register(); + Register(); Register(); } } // namespace tableau diff --git a/test/cpp-tableau-loader/src/protoconf/index_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/index_conf.pc.cc new file mode 100644 index 00000000..0e703d09 --- /dev/null +++ b/test/cpp-tableau-loader/src/protoconf/index_conf.pc.cc @@ -0,0 +1,420 @@ +// Code generated by protoc-gen-cpp-tableau-loader. DO NOT EDIT. +// versions: +// - protoc-gen-cpp-tableau-loader v0.10.1 +// - protoc v3.19.3 +// source: index_conf.proto + +#include "index_conf.pc.h" + +#include "hub.pc.h" +#include "util.pc.h" + +namespace tableau { +const std::string FruitConf::kProtoName = protoconf::FruitConf::GetDescriptor()->name(); + +bool FruitConf::Load(const std::filesystem::path& dir, Format fmt, std::shared_ptr options /* = nullptr */) { + tableau::util::TimeProfiler profiler; + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); + bool ok = loaded ? ProcessAfterLoad() : false; + stats_.duration = profiler.Elapse(); + return ok; +} + +bool FruitConf::ProcessAfterLoad() { + // OrderedIndex init. + ordered_index_item_map_.clear(); + ordered_index_item_map1_.clear(); + for (auto&& item1 : data_.fruit_map()) { + auto k1 = item1.first; + for (auto&& item2 : item1.second.item_map()) { + auto k2 = item2.first; + { + // OrderedIndex: Price + ordered_index_item_map_[item2.second.price()].push_back(&item2.second); + ordered_index_item_map1_[k1][item2.second.price()].push_back(&item2.second); + } + } + } + // OrderedIndex(sort): Price + auto ordered_index_item_map_sorter = [](const protoconf::FruitConf::Fruit::Item* a, + const protoconf::FruitConf::Fruit::Item* b) { + return a->id() < b->id(); + }; + for (auto&& item : ordered_index_item_map_) { + std::sort(item.second.begin(), item.second.end(), ordered_index_item_map_sorter); + } + for (auto&& item : ordered_index_item_map1_) { + for (auto&& item1 : item.second) { + std::sort(item1.second.begin(), item1.second.end(), ordered_index_item_map_sorter); + } + } + return true; +} + +const protoconf::FruitConf::Fruit* FruitConf::Get(int32_t fruit_type) const { + auto iter = data_.fruit_map().find(fruit_type); + if (iter == data_.fruit_map().end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::FruitConf::Fruit::Item* FruitConf::Get(int32_t fruit_type, int32_t id) const { + const auto* conf = Get(fruit_type); + if (conf == nullptr) { + return nullptr; + } + auto iter = conf->item_map().find(id); + if (iter == conf->item_map().end()) { + return nullptr; + } + return &iter->second; +} + +// OrderedIndex: Price +const FruitConf::OrderedIndex_ItemMap& FruitConf::FindItemMap() const { return ordered_index_item_map_ ;} + +const FruitConf::OrderedIndex_ItemVector* FruitConf::FindItem(int32_t price) const { + auto iter = ordered_index_item_map_.find(price); + if (iter == ordered_index_item_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::FruitConf::Fruit::Item* FruitConf::FindFirstItem(int32_t price) const { + auto conf = FindItem(price); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +const FruitConf::OrderedIndex_ItemMap* FruitConf::FindItemMap(int32_t fruit_type) const { + auto iter = ordered_index_item_map1_.find(fruit_type); + if (iter == ordered_index_item_map1_.end()) { + return nullptr; + } + return &iter->second; +} + +const FruitConf::OrderedIndex_ItemVector* FruitConf::FindItem(int32_t fruit_type, int32_t price) const { + auto map = FindItemMap(fruit_type); + if (map == nullptr) { + return nullptr; + } + auto iter = map->find(price); + if (iter == map->end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::FruitConf::Fruit::Item* FruitConf::FindFirstItem(int32_t fruit_type, int32_t price) const { + auto conf = FindItem(fruit_type, price); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +const std::string Fruit2Conf::kProtoName = protoconf::Fruit2Conf::GetDescriptor()->name(); + +bool Fruit2Conf::Load(const std::filesystem::path& dir, Format fmt, std::shared_ptr options /* = nullptr */) { + tableau::util::TimeProfiler profiler; + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); + bool ok = loaded ? ProcessAfterLoad() : false; + stats_.duration = profiler.Elapse(); + return ok; +} + +bool Fruit2Conf::ProcessAfterLoad() { + // Index init. + index_country_map_.clear(); + index_attr_map_.clear(); + index_attr_map1_.clear(); + for (auto&& item1 : data_.fruit_map()) { + auto k1 = item1.first; + for (auto&& item2 : item1.second.country_list()) { + { + // Index: CountryName + index_country_map_[item2.name()].push_back(&item2); + } + for (auto&& item3 : item2.item_map()) { + auto k2 = item3.first; + for (auto&& item4 : item3.second.attr_list()) { + { + // Index: CountryItemAttrName + index_attr_map_[item4.name()].push_back(&item4); + index_attr_map1_[k1][item4.name()].push_back(&item4); + } + } + } + } + } + // OrderedIndex init. + ordered_index_item_map_.clear(); + ordered_index_item_map1_.clear(); + for (auto&& item1 : data_.fruit_map()) { + auto k1 = item1.first; + for (auto&& item2 : item1.second.country_list()) { + for (auto&& item3 : item2.item_map()) { + auto k2 = item3.first; + { + // OrderedIndex: CountryItemPrice + ordered_index_item_map_[item3.second.price()].push_back(&item3.second); + ordered_index_item_map1_[k1][item3.second.price()].push_back(&item3.second); + } + } + } + } + // OrderedIndex(sort): CountryItemPrice + auto ordered_index_item_map_sorter = [](const protoconf::Fruit2Conf::Fruit::Country::Item* a, + const protoconf::Fruit2Conf::Fruit::Country::Item* b) { + return a->id() < b->id(); + }; + for (auto&& item : ordered_index_item_map_) { + std::sort(item.second.begin(), item.second.end(), ordered_index_item_map_sorter); + } + for (auto&& item : ordered_index_item_map1_) { + for (auto&& item1 : item.second) { + std::sort(item1.second.begin(), item1.second.end(), ordered_index_item_map_sorter); + } + } + return true; +} + +const protoconf::Fruit2Conf::Fruit* Fruit2Conf::Get(int32_t fruit_type) const { + auto iter = data_.fruit_map().find(fruit_type); + if (iter == data_.fruit_map().end()) { + return nullptr; + } + return &iter->second; +} + +// Index: CountryName +const Fruit2Conf::Index_CountryMap& Fruit2Conf::FindCountryMap() const { return index_country_map_ ;} + +const Fruit2Conf::Index_CountryVector* Fruit2Conf::FindCountry(const std::string& name) const { + auto iter = index_country_map_.find(name); + if (iter == index_country_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit2Conf::Fruit::Country* Fruit2Conf::FindFirstCountry(const std::string& name) const { + auto conf = FindCountry(name); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +// Index: CountryItemAttrName +const Fruit2Conf::Index_AttrMap& Fruit2Conf::FindAttrMap() const { return index_attr_map_ ;} + +const Fruit2Conf::Index_AttrVector* Fruit2Conf::FindAttr(const std::string& name) const { + auto iter = index_attr_map_.find(name); + if (iter == index_attr_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit2Conf::Fruit::Country::Item::Attr* Fruit2Conf::FindFirstAttr(const std::string& name) const { + auto conf = FindAttr(name); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +const Fruit2Conf::Index_AttrMap* Fruit2Conf::FindAttrMap(int32_t fruit_type) const { + auto iter = index_attr_map1_.find(fruit_type); + if (iter == index_attr_map1_.end()) { + return nullptr; + } + return &iter->second; +} + +const Fruit2Conf::Index_AttrVector* Fruit2Conf::FindAttr(int32_t fruit_type, const std::string& name) const { + auto map = FindAttrMap(fruit_type); + if (map == nullptr) { + return nullptr; + } + auto iter = map->find(name); + if (iter == map->end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit2Conf::Fruit::Country::Item::Attr* Fruit2Conf::FindFirstAttr(int32_t fruit_type, const std::string& name) const { + auto conf = FindAttr(fruit_type, name); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +// OrderedIndex: CountryItemPrice +const Fruit2Conf::OrderedIndex_ItemMap& Fruit2Conf::FindItemMap() const { return ordered_index_item_map_ ;} + +const Fruit2Conf::OrderedIndex_ItemVector* Fruit2Conf::FindItem(int32_t price) const { + auto iter = ordered_index_item_map_.find(price); + if (iter == ordered_index_item_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit2Conf::Fruit::Country::Item* Fruit2Conf::FindFirstItem(int32_t price) const { + auto conf = FindItem(price); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +const Fruit2Conf::OrderedIndex_ItemMap* Fruit2Conf::FindItemMap(int32_t fruit_type) const { + auto iter = ordered_index_item_map1_.find(fruit_type); + if (iter == ordered_index_item_map1_.end()) { + return nullptr; + } + return &iter->second; +} + +const Fruit2Conf::OrderedIndex_ItemVector* Fruit2Conf::FindItem(int32_t fruit_type, int32_t price) const { + auto map = FindItemMap(fruit_type); + if (map == nullptr) { + return nullptr; + } + auto iter = map->find(price); + if (iter == map->end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit2Conf::Fruit::Country::Item* Fruit2Conf::FindFirstItem(int32_t fruit_type, int32_t price) const { + auto conf = FindItem(fruit_type, price); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +const std::string Fruit3Conf::kProtoName = protoconf::Fruit3Conf::GetDescriptor()->name(); + +bool Fruit3Conf::Load(const std::filesystem::path& dir, Format fmt, std::shared_ptr options /* = nullptr */) { + tableau::util::TimeProfiler profiler; + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); + bool ok = loaded ? ProcessAfterLoad() : false; + stats_.duration = profiler.Elapse(); + return ok; +} + +bool Fruit3Conf::ProcessAfterLoad() { + // Index init. + index_country_map_.clear(); + index_attr_map_.clear(); + for (auto&& item1 : data_.fruit_list()) { + for (auto&& item2 : item1.country_list()) { + { + // Index: CountryName + index_country_map_[item2.name()].push_back(&item2); + } + for (auto&& item3 : item2.item_map()) { + auto k1 = item3.first; + for (auto&& item4 : item3.second.attr_list()) { + { + // Index: CountryItemAttrName + index_attr_map_[item4.name()].push_back(&item4); + } + } + } + } + } + // OrderedIndex init. + ordered_index_item_map_.clear(); + for (auto&& item1 : data_.fruit_list()) { + for (auto&& item2 : item1.country_list()) { + for (auto&& item3 : item2.item_map()) { + auto k1 = item3.first; + { + // OrderedIndex: CountryItemPrice + ordered_index_item_map_[item3.second.price()].push_back(&item3.second); + } + } + } + } + // OrderedIndex(sort): CountryItemPrice + auto ordered_index_item_map_sorter = [](const protoconf::Fruit3Conf::Fruit::Country::Item* a, + const protoconf::Fruit3Conf::Fruit::Country::Item* b) { + return a->id() < b->id(); + }; + for (auto&& item : ordered_index_item_map_) { + std::sort(item.second.begin(), item.second.end(), ordered_index_item_map_sorter); + } + return true; +} + +// Index: CountryName +const Fruit3Conf::Index_CountryMap& Fruit3Conf::FindCountryMap() const { return index_country_map_ ;} + +const Fruit3Conf::Index_CountryVector* Fruit3Conf::FindCountry(const std::string& name) const { + auto iter = index_country_map_.find(name); + if (iter == index_country_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit3Conf::Fruit::Country* Fruit3Conf::FindFirstCountry(const std::string& name) const { + auto conf = FindCountry(name); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +// Index: CountryItemAttrName +const Fruit3Conf::Index_AttrMap& Fruit3Conf::FindAttrMap() const { return index_attr_map_ ;} + +const Fruit3Conf::Index_AttrVector* Fruit3Conf::FindAttr(const std::string& name) const { + auto iter = index_attr_map_.find(name); + if (iter == index_attr_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit3Conf::Fruit::Country::Item::Attr* Fruit3Conf::FindFirstAttr(const std::string& name) const { + auto conf = FindAttr(name); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +// OrderedIndex: CountryItemPrice +const Fruit3Conf::OrderedIndex_ItemMap& Fruit3Conf::FindItemMap() const { return ordered_index_item_map_ ;} + +const Fruit3Conf::OrderedIndex_ItemVector* Fruit3Conf::FindItem(int32_t price) const { + auto iter = ordered_index_item_map_.find(price); + if (iter == ordered_index_item_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit3Conf::Fruit::Country::Item* Fruit3Conf::FindFirstItem(int32_t price) const { + auto conf = FindItem(price); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +} // namespace tableau diff --git a/test/cpp-tableau-loader/src/protoconf/index_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/index_conf.pc.h new file mode 100644 index 00000000..230d03d3 --- /dev/null +++ b/test/cpp-tableau-loader/src/protoconf/index_conf.pc.h @@ -0,0 +1,213 @@ +// Code generated by protoc-gen-cpp-tableau-loader. DO NOT EDIT. +// versions: +// - protoc-gen-cpp-tableau-loader v0.10.1 +// - protoc v3.19.3 +// source: index_conf.proto + +#pragma once +#include +#include + +#include "load.pc.h" +#include "util.pc.h" +#include "index_conf.pb.h" + +namespace tableau { +class FruitConf : public Messager { + public: + static const std::string& Name() { return kProtoName; } + virtual bool Load(const std::filesystem::path& dir, Format fmt, std::shared_ptr options = nullptr) override; + const protoconf::FruitConf& Data() const { return data_; } + const google::protobuf::Message* Message() const override { return &data_; } + + private: + virtual bool ProcessAfterLoad() override final; + + public: + const protoconf::FruitConf::Fruit* Get(int32_t fruit_type) const; + const protoconf::FruitConf::Fruit::Item* Get(int32_t fruit_type, int32_t id) const; + + private: + static const std::string kProtoName; + protoconf::FruitConf data_; + + // OrderedIndex accessers. + // OrderedIndex: Price + public: + using OrderedIndex_ItemVector = std::vector; + using OrderedIndex_ItemMap = std::map; + // Finds the ordered index: key(Price) to value(OrderedIndex_ItemVector) map. + // One key may correspond to multiple values, which are represented by a vector. + const OrderedIndex_ItemMap& FindItemMap() const; + // Finds a vector of all values of the given key(s). + const OrderedIndex_ItemVector* FindItem(int32_t price) const; + // Finds the first value of the given key(s). + const protoconf::FruitConf::Fruit::Item* FindFirstItem(int32_t price) const; + // Finds the ordered index: key(Price) to value(OrderedIndex_ItemVector), + // which is the upper 1st-level map specified by (fruit_type). + // One key may correspond to multiple values, which are represented by a vector. + const OrderedIndex_ItemMap* FindItemMap(int32_t fruit_type) const; + // Finds a vector of all values of the given key(s) in the upper 1st-level map specified by (fruit_type). + const OrderedIndex_ItemVector* FindItem(int32_t fruit_type, int32_t price) const; + // Finds the first value of the given key(s) in the upper 1st-level map specified by (fruit_type). + const protoconf::FruitConf::Fruit::Item* FindFirstItem(int32_t fruit_type, int32_t price) const; + + private: + OrderedIndex_ItemMap ordered_index_item_map_; + std::unordered_map ordered_index_item_map1_; +}; + +class Fruit2Conf : public Messager { + public: + static const std::string& Name() { return kProtoName; } + virtual bool Load(const std::filesystem::path& dir, Format fmt, std::shared_ptr options = nullptr) override; + const protoconf::Fruit2Conf& Data() const { return data_; } + const google::protobuf::Message* Message() const override { return &data_; } + + private: + virtual bool ProcessAfterLoad() override final; + + public: + const protoconf::Fruit2Conf::Fruit* Get(int32_t fruit_type) const; + + private: + static const std::string kProtoName; + protoconf::Fruit2Conf data_; + + // Index accessers. + // Index: CountryName + public: + using Index_CountryVector = std::vector; + using Index_CountryMap = std::unordered_map; + // Finds the index: key(CountryName) to value(Index_CountryVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_CountryMap& FindCountryMap() const; + // Finds a vector of all values of the given key(s). + const Index_CountryVector* FindCountry(const std::string& name) const; + // Finds the first value of the given key(s). + const protoconf::Fruit2Conf::Fruit::Country* FindFirstCountry(const std::string& name) const; + + private: + Index_CountryMap index_country_map_; + + // Index: CountryItemAttrName + public: + using Index_AttrVector = std::vector; + using Index_AttrMap = std::unordered_map; + // Finds the index: key(CountryItemAttrName) to value(Index_AttrVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_AttrMap& FindAttrMap() const; + // Finds a vector of all values of the given key(s). + const Index_AttrVector* FindAttr(const std::string& name) const; + // Finds the first value of the given key(s). + const protoconf::Fruit2Conf::Fruit::Country::Item::Attr* FindFirstAttr(const std::string& name) const; + // Finds the index: key(CountryItemAttrName) to value(Index_AttrVector), + // which is the upper 1st-level hashmap specified by (fruit_type). + // One key may correspond to multiple values, which are represented by a vector. + const Index_AttrMap* FindAttrMap(int32_t fruit_type) const; + // Finds a vector of all values of the given key(s) in the upper 1st-level hashmap specified by (fruit_type). + const Index_AttrVector* FindAttr(int32_t fruit_type, const std::string& name) const; + // Finds the first value of the given key(s) in the upper 1st-level hashmap specified by (fruit_type). + const protoconf::Fruit2Conf::Fruit::Country::Item::Attr* FindFirstAttr(int32_t fruit_type, const std::string& name) const; + + private: + Index_AttrMap index_attr_map_; + std::unordered_map index_attr_map1_; + + // OrderedIndex accessers. + // OrderedIndex: CountryItemPrice + public: + using OrderedIndex_ItemVector = std::vector; + using OrderedIndex_ItemMap = std::map; + // Finds the ordered index: key(CountryItemPrice) to value(OrderedIndex_ItemVector) map. + // One key may correspond to multiple values, which are represented by a vector. + const OrderedIndex_ItemMap& FindItemMap() const; + // Finds a vector of all values of the given key(s). + const OrderedIndex_ItemVector* FindItem(int32_t price) const; + // Finds the first value of the given key(s). + const protoconf::Fruit2Conf::Fruit::Country::Item* FindFirstItem(int32_t price) const; + // Finds the ordered index: key(CountryItemPrice) to value(OrderedIndex_ItemVector), + // which is the upper 1st-level map specified by (fruit_type). + // One key may correspond to multiple values, which are represented by a vector. + const OrderedIndex_ItemMap* FindItemMap(int32_t fruit_type) const; + // Finds a vector of all values of the given key(s) in the upper 1st-level map specified by (fruit_type). + const OrderedIndex_ItemVector* FindItem(int32_t fruit_type, int32_t price) const; + // Finds the first value of the given key(s) in the upper 1st-level map specified by (fruit_type). + const protoconf::Fruit2Conf::Fruit::Country::Item* FindFirstItem(int32_t fruit_type, int32_t price) const; + + private: + OrderedIndex_ItemMap ordered_index_item_map_; + std::unordered_map ordered_index_item_map1_; +}; + +class Fruit3Conf : public Messager { + public: + static const std::string& Name() { return kProtoName; } + virtual bool Load(const std::filesystem::path& dir, Format fmt, std::shared_ptr options = nullptr) override; + const protoconf::Fruit3Conf& Data() const { return data_; } + const google::protobuf::Message* Message() const override { return &data_; } + + private: + virtual bool ProcessAfterLoad() override final; + + + private: + static const std::string kProtoName; + protoconf::Fruit3Conf data_; + + // Index accessers. + // Index: CountryName + public: + using Index_CountryVector = std::vector; + using Index_CountryMap = std::unordered_map; + // Finds the index: key(CountryName) to value(Index_CountryVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_CountryMap& FindCountryMap() const; + // Finds a vector of all values of the given key(s). + const Index_CountryVector* FindCountry(const std::string& name) const; + // Finds the first value of the given key(s). + const protoconf::Fruit3Conf::Fruit::Country* FindFirstCountry(const std::string& name) const; + + private: + Index_CountryMap index_country_map_; + + // Index: CountryItemAttrName + public: + using Index_AttrVector = std::vector; + using Index_AttrMap = std::unordered_map; + // Finds the index: key(CountryItemAttrName) to value(Index_AttrVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_AttrMap& FindAttrMap() const; + // Finds a vector of all values of the given key(s). + const Index_AttrVector* FindAttr(const std::string& name) const; + // Finds the first value of the given key(s). + const protoconf::Fruit3Conf::Fruit::Country::Item::Attr* FindFirstAttr(const std::string& name) const; + + private: + Index_AttrMap index_attr_map_; + + // OrderedIndex accessers. + // OrderedIndex: CountryItemPrice + public: + using OrderedIndex_ItemVector = std::vector; + using OrderedIndex_ItemMap = std::map; + // Finds the ordered index: key(CountryItemPrice) to value(OrderedIndex_ItemVector) map. + // One key may correspond to multiple values, which are represented by a vector. + const OrderedIndex_ItemMap& FindItemMap() const; + // Finds a vector of all values of the given key(s). + const OrderedIndex_ItemVector* FindItem(int32_t price) const; + // Finds the first value of the given key(s). + const protoconf::Fruit3Conf::Fruit::Country::Item* FindFirstItem(int32_t price) const; + + private: + OrderedIndex_ItemMap ordered_index_item_map_; +}; + +} // namespace tableau + +namespace protoconf { +// Here are some type aliases for easy use. +using FruitConfMgr = tableau::FruitConf; +using Fruit2ConfMgr = tableau::Fruit2Conf; +using Fruit3ConfMgr = tableau::Fruit3Conf; +} // namespace protoconf diff --git a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc index 2e3ceff6..6216a72c 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc @@ -38,6 +38,7 @@ bool ItemConf::ProcessAfterLoad() { index_item_path_friend_id_map_.clear(); index_use_effect_type_map_.clear(); for (auto&& item1 : data_.item_map()) { + auto k1 = item1.first; { // Index: Type index_item_map_[item1.second.type()].push_back(&item1.second); @@ -92,26 +93,29 @@ bool ItemConf::ProcessAfterLoad() { } } // Index(sort): Param@ItemInfo + auto index_item_info_map_sorter = [](const protoconf::ItemConf::Item* a, + const protoconf::ItemConf::Item* b) { + return a->id() < b->id(); + }; for (auto&& item : index_item_info_map_) { - std::sort(item.second.begin(), item.second.end(), - [](const protoconf::ItemConf::Item* a, const protoconf::ItemConf::Item* b) { - return a->id() < b->id(); - }); + std::sort(item.second.begin(), item.second.end(), index_item_info_map_sorter); } // Index(sort): (ID,Name)@AwardItem + auto index_award_item_map_sorter = [](const protoconf::ItemConf::Item* a, + const protoconf::ItemConf::Item* b) { + if (a->type() != b->type()) { + return a->type() < b->type(); + } + return a->use_effect().type() < b->use_effect().type(); + }; for (auto&& item : index_award_item_map_) { - std::sort(item.second.begin(), item.second.end(), - [](const protoconf::ItemConf::Item* a, const protoconf::ItemConf::Item* b) { - if (a->type() != b->type()) { - return a->type() < b->type(); - } - return a->use_effect().type() < b->use_effect().type(); - }); + std::sort(item.second.begin(), item.second.end(), index_award_item_map_sorter); } // OrderedIndex init. ordered_index_ext_type_map_.clear(); ordered_index_param_ext_type_map_.clear(); for (auto&& item1 : data_.item_map()) { + auto k1 = item1.first; { // OrderedIndex: ExtType@ExtType for (auto&& item2 : item1.second.ext_type_list()) { @@ -129,11 +133,12 @@ bool ItemConf::ProcessAfterLoad() { } } // OrderedIndex(sort): (Param,ExtType)@ParamExtType + auto ordered_index_param_ext_type_map_sorter = [](const protoconf::ItemConf::Item* a, + const protoconf::ItemConf::Item* b) { + return a->id() < b->id(); + }; for (auto&& item : ordered_index_param_ext_type_map_) { - std::sort(item.second.begin(), item.second.end(), - [](const protoconf::ItemConf::Item* a, const protoconf::ItemConf::Item* b) { - return a->id() < b->id(); - }); + std::sort(item.second.begin(), item.second.end(), ordered_index_param_ext_type_map_sorter); } return true; } @@ -151,7 +156,7 @@ const ItemConf::OrderedMap_ItemMap* ItemConf::GetOrderedMap() const { } // Index: Type -const ItemConf::Index_ItemMap& ItemConf::FindItem() const { return index_item_map_ ;} +const ItemConf::Index_ItemMap& ItemConf::FindItemMap() const { return index_item_map_ ;} const ItemConf::Index_ItemVector* ItemConf::FindItem(protoconf::FruitType type) const { auto iter = index_item_map_.find(type); @@ -170,7 +175,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItem(protoconf::FruitType ty } // Index: Param@ItemInfo -const ItemConf::Index_ItemInfoMap& ItemConf::FindItemInfo() const { return index_item_info_map_ ;} +const ItemConf::Index_ItemInfoMap& ItemConf::FindItemInfoMap() const { return index_item_info_map_ ;} const ItemConf::Index_ItemInfoVector* ItemConf::FindItemInfo(int32_t param) const { auto iter = index_item_info_map_.find(param); @@ -189,7 +194,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemInfo(int32_t param) cons } // Index: Default@ItemDefaultInfo -const ItemConf::Index_ItemDefaultInfoMap& ItemConf::FindItemDefaultInfo() const { return index_item_default_info_map_ ;} +const ItemConf::Index_ItemDefaultInfoMap& ItemConf::FindItemDefaultInfoMap() const { return index_item_default_info_map_ ;} const ItemConf::Index_ItemDefaultInfoVector* ItemConf::FindItemDefaultInfo(const std::string& default_) const { auto iter = index_item_default_info_map_.find(default_); @@ -208,7 +213,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemDefaultInfo(const std::s } // Index: ExtType@ItemExtInfo -const ItemConf::Index_ItemExtInfoMap& ItemConf::FindItemExtInfo() const { return index_item_ext_info_map_ ;} +const ItemConf::Index_ItemExtInfoMap& ItemConf::FindItemExtInfoMap() const { return index_item_ext_info_map_ ;} const ItemConf::Index_ItemExtInfoVector* ItemConf::FindItemExtInfo(protoconf::FruitType ext_type) const { auto iter = index_item_ext_info_map_.find(ext_type); @@ -227,7 +232,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemExtInfo(protoconf::Fruit } // Index: (ID,Name)@AwardItem -const ItemConf::Index_AwardItemMap& ItemConf::FindAwardItem() const { return index_award_item_map_ ;} +const ItemConf::Index_AwardItemMap& ItemConf::FindAwardItemMap() const { return index_award_item_map_ ;} const ItemConf::Index_AwardItemVector* ItemConf::FindAwardItem(uint32_t id, const std::string& name) const { auto iter = index_award_item_map_.find({id, name}); @@ -246,7 +251,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstAwardItem(uint32_t id, const } // Index: (ID,Type,Param,ExtType)@SpecialItem -const ItemConf::Index_SpecialItemMap& ItemConf::FindSpecialItem() const { return index_special_item_map_ ;} +const ItemConf::Index_SpecialItemMap& ItemConf::FindSpecialItemMap() const { return index_special_item_map_ ;} const ItemConf::Index_SpecialItemVector* ItemConf::FindSpecialItem(uint32_t id, protoconf::FruitType type, int32_t param, protoconf::FruitType ext_type) const { auto iter = index_special_item_map_.find({id, type, param, ext_type}); @@ -265,7 +270,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstSpecialItem(uint32_t id, pro } // Index: PathDir@ItemPathDir -const ItemConf::Index_ItemPathDirMap& ItemConf::FindItemPathDir() const { return index_item_path_dir_map_ ;} +const ItemConf::Index_ItemPathDirMap& ItemConf::FindItemPathDirMap() const { return index_item_path_dir_map_ ;} const ItemConf::Index_ItemPathDirVector* ItemConf::FindItemPathDir(const std::string& dir) const { auto iter = index_item_path_dir_map_.find(dir); @@ -284,7 +289,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemPathDir(const std::strin } // Index: PathName@ItemPathName -const ItemConf::Index_ItemPathNameMap& ItemConf::FindItemPathName() const { return index_item_path_name_map_ ;} +const ItemConf::Index_ItemPathNameMap& ItemConf::FindItemPathNameMap() const { return index_item_path_name_map_ ;} const ItemConf::Index_ItemPathNameVector* ItemConf::FindItemPathName(const std::string& name) const { auto iter = index_item_path_name_map_.find(name); @@ -303,7 +308,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemPathName(const std::stri } // Index: PathFriendID@ItemPathFriendID -const ItemConf::Index_ItemPathFriendIDMap& ItemConf::FindItemPathFriendID() const { return index_item_path_friend_id_map_ ;} +const ItemConf::Index_ItemPathFriendIDMap& ItemConf::FindItemPathFriendIDMap() const { return index_item_path_friend_id_map_ ;} const ItemConf::Index_ItemPathFriendIDVector* ItemConf::FindItemPathFriendID(uint32_t id) const { auto iter = index_item_path_friend_id_map_.find(id); @@ -322,7 +327,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemPathFriendID(uint32_t id } // Index: UseEffectType@UseEffectType -const ItemConf::Index_UseEffectTypeMap& ItemConf::FindUseEffectType() const { return index_use_effect_type_map_ ;} +const ItemConf::Index_UseEffectTypeMap& ItemConf::FindUseEffectTypeMap() const { return index_use_effect_type_map_ ;} const ItemConf::Index_UseEffectTypeVector* ItemConf::FindUseEffectType(protoconf::UseEffect::Type type) const { auto iter = index_use_effect_type_map_.find(type); @@ -341,7 +346,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstUseEffectType(protoconf::Use } // OrderedIndex: ExtType@ExtType -const ItemConf::OrderedIndex_ExtTypeMap& ItemConf::FindExtType() const { return ordered_index_ext_type_map_ ;} +const ItemConf::OrderedIndex_ExtTypeMap& ItemConf::FindExtTypeMap() const { return ordered_index_ext_type_map_ ;} const ItemConf::OrderedIndex_ExtTypeVector* ItemConf::FindExtType(protoconf::FruitType ext_type) const { auto iter = ordered_index_ext_type_map_.find(ext_type); @@ -360,7 +365,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstExtType(protoconf::FruitType } // OrderedIndex: (Param,ExtType)@ParamExtType -const ItemConf::OrderedIndex_ParamExtTypeMap& ItemConf::FindParamExtType() const { return ordered_index_param_ext_type_map_ ;} +const ItemConf::OrderedIndex_ParamExtTypeMap& ItemConf::FindParamExtTypeMap() const { return ordered_index_param_ext_type_map_ ;} const ItemConf::OrderedIndex_ParamExtTypeVector* ItemConf::FindParamExtType(int32_t param, protoconf::FruitType ext_type) const { auto iter = ordered_index_param_ext_type_map_.find({param, ext_type}); diff --git a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h index 56d1bff8..543816b2 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h @@ -43,9 +43,9 @@ class ItemConf : public Messager { public: using Index_ItemVector = std::vector; using Index_ItemMap = std::unordered_map; - // Finds the index (Type) to value (Index_ItemVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemMap& FindItem() const; + // Finds the index: key(Type) to value(Index_ItemVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_ItemMap& FindItemMap() const; // Finds a vector of all values of the given key(s). const Index_ItemVector* FindItem(protoconf::FruitType type) const; // Finds the first value of the given key(s). @@ -58,9 +58,9 @@ class ItemConf : public Messager { public: using Index_ItemInfoVector = std::vector; using Index_ItemInfoMap = std::unordered_map; - // Finds the index (Param@ItemInfo) to value (Index_ItemInfoVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemInfoMap& FindItemInfo() const; + // Finds the index: key(Param@ItemInfo) to value(Index_ItemInfoVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_ItemInfoMap& FindItemInfoMap() const; // Finds a vector of all values of the given key(s). const Index_ItemInfoVector* FindItemInfo(int32_t param) const; // Finds the first value of the given key(s). @@ -73,9 +73,9 @@ class ItemConf : public Messager { public: using Index_ItemDefaultInfoVector = std::vector; using Index_ItemDefaultInfoMap = std::unordered_map; - // Finds the index (Default@ItemDefaultInfo) to value (Index_ItemDefaultInfoVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemDefaultInfoMap& FindItemDefaultInfo() const; + // Finds the index: key(Default@ItemDefaultInfo) to value(Index_ItemDefaultInfoVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_ItemDefaultInfoMap& FindItemDefaultInfoMap() const; // Finds a vector of all values of the given key(s). const Index_ItemDefaultInfoVector* FindItemDefaultInfo(const std::string& default_) const; // Finds the first value of the given key(s). @@ -88,9 +88,9 @@ class ItemConf : public Messager { public: using Index_ItemExtInfoVector = std::vector; using Index_ItemExtInfoMap = std::unordered_map; - // Finds the index (ExtType@ItemExtInfo) to value (Index_ItemExtInfoVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemExtInfoMap& FindItemExtInfo() const; + // Finds the index: key(ExtType@ItemExtInfo) to value(Index_ItemExtInfoVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_ItemExtInfoMap& FindItemExtInfoMap() const; // Finds a vector of all values of the given key(s). const Index_ItemExtInfoVector* FindItemExtInfo(protoconf::FruitType ext_type) const; // Finds the first value of the given key(s). @@ -119,9 +119,9 @@ class ItemConf : public Messager { }; using Index_AwardItemVector = std::vector; using Index_AwardItemMap = std::unordered_map; - // Finds the index ((ID,Name)@AwardItem) to value (Index_AwardItemVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_AwardItemMap& FindAwardItem() const; + // Finds the index: key((ID,Name)@AwardItem) to value(Index_AwardItemVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_AwardItemMap& FindAwardItemMap() const; // Finds a vector of all values of the given key(s). const Index_AwardItemVector* FindAwardItem(uint32_t id, const std::string& name) const; // Finds the first value of the given key(s). @@ -152,9 +152,9 @@ class ItemConf : public Messager { }; using Index_SpecialItemVector = std::vector; using Index_SpecialItemMap = std::unordered_map; - // Finds the index ((ID,Type,Param,ExtType)@SpecialItem) to value (Index_SpecialItemVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_SpecialItemMap& FindSpecialItem() const; + // Finds the index: key((ID,Type,Param,ExtType)@SpecialItem) to value(Index_SpecialItemVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_SpecialItemMap& FindSpecialItemMap() const; // Finds a vector of all values of the given key(s). const Index_SpecialItemVector* FindSpecialItem(uint32_t id, protoconf::FruitType type, int32_t param, protoconf::FruitType ext_type) const; // Finds the first value of the given key(s). @@ -167,9 +167,9 @@ class ItemConf : public Messager { public: using Index_ItemPathDirVector = std::vector; using Index_ItemPathDirMap = std::unordered_map; - // Finds the index (PathDir@ItemPathDir) to value (Index_ItemPathDirVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemPathDirMap& FindItemPathDir() const; + // Finds the index: key(PathDir@ItemPathDir) to value(Index_ItemPathDirVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_ItemPathDirMap& FindItemPathDirMap() const; // Finds a vector of all values of the given key(s). const Index_ItemPathDirVector* FindItemPathDir(const std::string& dir) const; // Finds the first value of the given key(s). @@ -182,9 +182,9 @@ class ItemConf : public Messager { public: using Index_ItemPathNameVector = std::vector; using Index_ItemPathNameMap = std::unordered_map; - // Finds the index (PathName@ItemPathName) to value (Index_ItemPathNameVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemPathNameMap& FindItemPathName() const; + // Finds the index: key(PathName@ItemPathName) to value(Index_ItemPathNameVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_ItemPathNameMap& FindItemPathNameMap() const; // Finds a vector of all values of the given key(s). const Index_ItemPathNameVector* FindItemPathName(const std::string& name) const; // Finds the first value of the given key(s). @@ -197,9 +197,9 @@ class ItemConf : public Messager { public: using Index_ItemPathFriendIDVector = std::vector; using Index_ItemPathFriendIDMap = std::unordered_map; - // Finds the index (PathFriendID@ItemPathFriendID) to value (Index_ItemPathFriendIDVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemPathFriendIDMap& FindItemPathFriendID() const; + // Finds the index: key(PathFriendID@ItemPathFriendID) to value(Index_ItemPathFriendIDVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_ItemPathFriendIDMap& FindItemPathFriendIDMap() const; // Finds a vector of all values of the given key(s). const Index_ItemPathFriendIDVector* FindItemPathFriendID(uint32_t id) const; // Finds the first value of the given key(s). @@ -212,9 +212,9 @@ class ItemConf : public Messager { public: using Index_UseEffectTypeVector = std::vector; using Index_UseEffectTypeMap = std::unordered_map; - // Finds the index (UseEffectType@UseEffectType) to value (Index_UseEffectTypeVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_UseEffectTypeMap& FindUseEffectType() const; + // Finds the index: key(UseEffectType@UseEffectType) to value(Index_UseEffectTypeVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_UseEffectTypeMap& FindUseEffectTypeMap() const; // Finds a vector of all values of the given key(s). const Index_UseEffectTypeVector* FindUseEffectType(protoconf::UseEffect::Type type) const; // Finds the first value of the given key(s). @@ -228,9 +228,9 @@ class ItemConf : public Messager { public: using OrderedIndex_ExtTypeVector = std::vector; using OrderedIndex_ExtTypeMap = std::map; - // Finds the ordered index (ExtType@ExtType) to value (OrderedIndex_ExtTypeVector) map. - // One key may correspond to multiple values, which are contained by a vector. - const OrderedIndex_ExtTypeMap& FindExtType() const; + // Finds the ordered index: key(ExtType@ExtType) to value(OrderedIndex_ExtTypeVector) map. + // One key may correspond to multiple values, which are represented by a vector. + const OrderedIndex_ExtTypeMap& FindExtTypeMap() const; // Finds a vector of all values of the given key(s). const OrderedIndex_ExtTypeVector* FindExtType(protoconf::FruitType ext_type) const; // Finds the first value of the given key(s). @@ -254,9 +254,9 @@ class ItemConf : public Messager { }; using OrderedIndex_ParamExtTypeVector = std::vector; using OrderedIndex_ParamExtTypeMap = std::map; - // Finds the ordered index ((Param,ExtType)@ParamExtType) to value (OrderedIndex_ParamExtTypeVector) map. - // One key may correspond to multiple values, which are contained by a vector. - const OrderedIndex_ParamExtTypeMap& FindParamExtType() const; + // Finds the ordered index: key((Param,ExtType)@ParamExtType) to value(OrderedIndex_ParamExtTypeVector) map. + // One key may correspond to multiple values, which are represented by a vector. + const OrderedIndex_ParamExtTypeMap& FindParamExtTypeMap() const; // Finds a vector of all values of the given key(s). const OrderedIndex_ParamExtTypeVector* FindParamExtType(int32_t param, protoconf::FruitType ext_type) const; // Finds the first value of the given key(s). @@ -264,7 +264,6 @@ class ItemConf : public Messager { private: OrderedIndex_ParamExtTypeMap ordered_index_param_ext_type_map_; - }; } // namespace tableau diff --git a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc index 1018fd65..bcd76c15 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc @@ -41,38 +41,55 @@ bool ActivityConf::ProcessAfterLoad() { // Index init. index_activity_map_.clear(); index_chapter_map_.clear(); + index_chapter_map1_.clear(); index_named_chapter_map_.clear(); + index_named_chapter_map1_.clear(); index_award_map_.clear(); + index_award_map1_.clear(); + index_award_map2_.clear(); for (auto&& item1 : data_.activity_map()) { + auto k1 = item1.first; { // Index: ActivityName index_activity_map_[item1.second.activity_name()].push_back(&item1.second); } for (auto&& item2 : item1.second.chapter_map()) { + auto k2 = item2.first; { // Index: ChapterID index_chapter_map_[item2.second.chapter_id()].push_back(&item2.second); + index_chapter_map1_[k1][item2.second.chapter_id()].push_back(&item2.second); } { // Index: ChapterName@NamedChapter index_named_chapter_map_[item2.second.chapter_name()].push_back(&item2.second); + index_named_chapter_map1_[k1][item2.second.chapter_name()].push_back(&item2.second); } for (auto&& item3 : item2.second.section_map()) { + auto k3 = item3.first; for (auto&& item4 : item3.second.section_item_list()) { { // Index: SectionItemID@Award index_award_map_[item4.id()].push_back(&item4); + index_award_map1_[k1][item4.id()].push_back(&item4); + index_award_map2_[{k1, k2}][item4.id()].push_back(&item4); } } } } } // Index(sort): ChapterName@NamedChapter + auto index_named_chapter_map_sorter = [](const protoconf::ActivityConf::Activity::Chapter* a, + const protoconf::ActivityConf::Activity::Chapter* b) { + return a->award_id() < b->award_id(); + }; for (auto&& item : index_named_chapter_map_) { - std::sort(item.second.begin(), item.second.end(), - [](const protoconf::ActivityConf::Activity::Chapter* a, const protoconf::ActivityConf::Activity::Chapter* b) { - return a->award_id() < b->award_id(); - }); + std::sort(item.second.begin(), item.second.end(), index_named_chapter_map_sorter); + } + for (auto&& item : index_named_chapter_map1_) { + for (auto&& item1 : item.second) { + std::sort(item1.second.begin(), item1.second.end(), index_named_chapter_map_sorter); + } } return true; } @@ -162,7 +179,7 @@ const ActivityConf::OrderedMap_int32Map* ActivityConf::GetOrderedMap(uint64_t ac } // Index: ActivityName -const ActivityConf::Index_ActivityMap& ActivityConf::FindActivity() const { return index_activity_map_ ;} +const ActivityConf::Index_ActivityMap& ActivityConf::FindActivityMap() const { return index_activity_map_ ;} const ActivityConf::Index_ActivityVector* ActivityConf::FindActivity(const std::string& activity_name) const { auto iter = index_activity_map_.find(activity_name); @@ -181,7 +198,7 @@ const protoconf::ActivityConf::Activity* ActivityConf::FindFirstActivity(const s } // Index: ChapterID -const ActivityConf::Index_ChapterMap& ActivityConf::FindChapter() const { return index_chapter_map_ ;} +const ActivityConf::Index_ChapterMap& ActivityConf::FindChapterMap() const { return index_chapter_map_ ;} const ActivityConf::Index_ChapterVector* ActivityConf::FindChapter(uint32_t chapter_id) const { auto iter = index_chapter_map_.find(chapter_id); @@ -199,8 +216,36 @@ const protoconf::ActivityConf::Activity::Chapter* ActivityConf::FindFirstChapter return conf->front(); } +const ActivityConf::Index_ChapterMap* ActivityConf::FindChapterMap(uint64_t activity_id) const { + auto iter = index_chapter_map1_.find(activity_id); + if (iter == index_chapter_map1_.end()) { + return nullptr; + } + return &iter->second; +} + +const ActivityConf::Index_ChapterVector* ActivityConf::FindChapter(uint64_t activity_id, uint32_t chapter_id) const { + auto map = FindChapterMap(activity_id); + if (map == nullptr) { + return nullptr; + } + auto iter = map->find(chapter_id); + if (iter == map->end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::ActivityConf::Activity::Chapter* ActivityConf::FindFirstChapter(uint64_t activity_id, uint32_t chapter_id) const { + auto conf = FindChapter(activity_id, chapter_id); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + // Index: ChapterName@NamedChapter -const ActivityConf::Index_NamedChapterMap& ActivityConf::FindNamedChapter() const { return index_named_chapter_map_ ;} +const ActivityConf::Index_NamedChapterMap& ActivityConf::FindNamedChapterMap() const { return index_named_chapter_map_ ;} const ActivityConf::Index_NamedChapterVector* ActivityConf::FindNamedChapter(const std::string& chapter_name) const { auto iter = index_named_chapter_map_.find(chapter_name); @@ -218,8 +263,36 @@ const protoconf::ActivityConf::Activity::Chapter* ActivityConf::FindFirstNamedCh return conf->front(); } +const ActivityConf::Index_NamedChapterMap* ActivityConf::FindNamedChapterMap(uint64_t activity_id) const { + auto iter = index_named_chapter_map1_.find(activity_id); + if (iter == index_named_chapter_map1_.end()) { + return nullptr; + } + return &iter->second; +} + +const ActivityConf::Index_NamedChapterVector* ActivityConf::FindNamedChapter(uint64_t activity_id, const std::string& chapter_name) const { + auto map = FindNamedChapterMap(activity_id); + if (map == nullptr) { + return nullptr; + } + auto iter = map->find(chapter_name); + if (iter == map->end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::ActivityConf::Activity::Chapter* ActivityConf::FindFirstNamedChapter(uint64_t activity_id, const std::string& chapter_name) const { + auto conf = FindNamedChapter(activity_id, chapter_name); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + // Index: SectionItemID@Award -const ActivityConf::Index_AwardMap& ActivityConf::FindAward() const { return index_award_map_ ;} +const ActivityConf::Index_AwardMap& ActivityConf::FindAwardMap() const { return index_award_map_ ;} const ActivityConf::Index_AwardVector* ActivityConf::FindAward(uint32_t id) const { auto iter = index_award_map_.find(id); @@ -237,6 +310,62 @@ const protoconf::Section::SectionItem* ActivityConf::FindFirstAward(uint32_t id) return conf->front(); } +const ActivityConf::Index_AwardMap* ActivityConf::FindAwardMap(uint64_t activity_id) const { + auto iter = index_award_map1_.find(activity_id); + if (iter == index_award_map1_.end()) { + return nullptr; + } + return &iter->second; +} + +const ActivityConf::Index_AwardVector* ActivityConf::FindAward(uint64_t activity_id, uint32_t id) const { + auto map = FindAwardMap(activity_id); + if (map == nullptr) { + return nullptr; + } + auto iter = map->find(id); + if (iter == map->end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Section::SectionItem* ActivityConf::FindFirstAward(uint64_t activity_id, uint32_t id) const { + auto conf = FindAward(activity_id, id); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +const ActivityConf::Index_AwardMap* ActivityConf::FindAwardMap(uint64_t activity_id, uint32_t chapter_id) const { + auto iter = index_award_map2_.find({activity_id, chapter_id}); + if (iter == index_award_map2_.end()) { + return nullptr; + } + return &iter->second; +} + +const ActivityConf::Index_AwardVector* ActivityConf::FindAward(uint64_t activity_id, uint32_t chapter_id, uint32_t id) const { + auto map = FindAwardMap(activity_id, chapter_id); + if (map == nullptr) { + return nullptr; + } + auto iter = map->find(id); + if (iter == map->end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Section::SectionItem* ActivityConf::FindFirstAward(uint64_t activity_id, uint32_t chapter_id, uint32_t id) const { + auto conf = FindAward(activity_id, chapter_id, id); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + const std::string ChapterConf::kProtoName = protoconf::ChapterConf::GetDescriptor()->name(); bool ChapterConf::Load(const std::filesystem::path& dir, Format fmt, std::shared_ptr options /* = nullptr */) { @@ -299,20 +428,22 @@ bool TaskConf::ProcessAfterLoad() { // Index init. index_task_map_.clear(); for (auto&& item1 : data_.task_map()) { + auto k1 = item1.first; { // Index: ActivityID index_task_map_[item1.second.activity_id()].push_back(&item1.second); } } // Index(sort): ActivityID + auto index_task_map_sorter = [](const protoconf::TaskConf::Task* a, + const protoconf::TaskConf::Task* b) { + if (a->goal() != b->goal()) { + return a->goal() < b->goal(); + } + return a->id() < b->id(); + }; for (auto&& item : index_task_map_) { - std::sort(item.second.begin(), item.second.end(), - [](const protoconf::TaskConf::Task* a, const protoconf::TaskConf::Task* b) { - if (a->goal() != b->goal()) { - return a->goal() < b->goal(); - } - return a->id() < b->id(); - }); + std::sort(item.second.begin(), item.second.end(), index_task_map_sorter); } // OrderedIndex init. ordered_index_ordered_task_map_.clear(); @@ -320,6 +451,7 @@ bool TaskConf::ProcessAfterLoad() { ordered_index_sorted_task_expiry_map_.clear(); ordered_index_activity_expiry_map_.clear(); for (auto&& item1 : data_.task_map()) { + auto k1 = item1.first; { // OrderedIndex: Goal@OrderedTask ordered_index_ordered_task_map_[item1.second.goal()].push_back(&item1.second); @@ -339,21 +471,23 @@ bool TaskConf::ProcessAfterLoad() { } } // OrderedIndex(sort): Goal@OrderedTask + auto ordered_index_ordered_task_map_sorter = [](const protoconf::TaskConf::Task* a, + const protoconf::TaskConf::Task* b) { + return a->id() < b->id(); + }; for (auto&& item : ordered_index_ordered_task_map_) { - std::sort(item.second.begin(), item.second.end(), - [](const protoconf::TaskConf::Task* a, const protoconf::TaskConf::Task* b) { - return a->id() < b->id(); - }); + std::sort(item.second.begin(), item.second.end(), ordered_index_ordered_task_map_sorter); } // OrderedIndex(sort): Expiry@SortedTaskExpiry + auto ordered_index_sorted_task_expiry_map_sorter = [](const protoconf::TaskConf::Task* a, + const protoconf::TaskConf::Task* b) { + if (a->goal() != b->goal()) { + return a->goal() < b->goal(); + } + return a->id() < b->id(); + }; for (auto&& item : ordered_index_sorted_task_expiry_map_) { - std::sort(item.second.begin(), item.second.end(), - [](const protoconf::TaskConf::Task* a, const protoconf::TaskConf::Task* b) { - if (a->goal() != b->goal()) { - return a->goal() < b->goal(); - } - return a->id() < b->id(); - }); + std::sort(item.second.begin(), item.second.end(), ordered_index_sorted_task_expiry_map_sorter); } return true; } @@ -367,7 +501,7 @@ const protoconf::TaskConf::Task* TaskConf::Get(int64_t id) const { } // Index: ActivityID -const TaskConf::Index_TaskMap& TaskConf::FindTask() const { return index_task_map_ ;} +const TaskConf::Index_TaskMap& TaskConf::FindTaskMap() const { return index_task_map_ ;} const TaskConf::Index_TaskVector* TaskConf::FindTask(int64_t activity_id) const { auto iter = index_task_map_.find(activity_id); @@ -386,7 +520,7 @@ const protoconf::TaskConf::Task* TaskConf::FindFirstTask(int64_t activity_id) co } // OrderedIndex: Goal@OrderedTask -const TaskConf::OrderedIndex_OrderedTaskMap& TaskConf::FindOrderedTask() const { return ordered_index_ordered_task_map_ ;} +const TaskConf::OrderedIndex_OrderedTaskMap& TaskConf::FindOrderedTaskMap() const { return ordered_index_ordered_task_map_ ;} const TaskConf::OrderedIndex_OrderedTaskVector* TaskConf::FindOrderedTask(int64_t goal) const { auto iter = ordered_index_ordered_task_map_.find(goal); @@ -405,7 +539,7 @@ const protoconf::TaskConf::Task* TaskConf::FindFirstOrderedTask(int64_t goal) co } // OrderedIndex: Expiry@TaskExpiry -const TaskConf::OrderedIndex_TaskExpiryMap& TaskConf::FindTaskExpiry() const { return ordered_index_task_expiry_map_ ;} +const TaskConf::OrderedIndex_TaskExpiryMap& TaskConf::FindTaskExpiryMap() const { return ordered_index_task_expiry_map_ ;} const TaskConf::OrderedIndex_TaskExpiryVector* TaskConf::FindTaskExpiry(int64_t expiry) const { auto iter = ordered_index_task_expiry_map_.find(expiry); @@ -424,7 +558,7 @@ const protoconf::TaskConf::Task* TaskConf::FindFirstTaskExpiry(int64_t expiry) c } // OrderedIndex: Expiry@SortedTaskExpiry -const TaskConf::OrderedIndex_SortedTaskExpiryMap& TaskConf::FindSortedTaskExpiry() const { return ordered_index_sorted_task_expiry_map_ ;} +const TaskConf::OrderedIndex_SortedTaskExpiryMap& TaskConf::FindSortedTaskExpiryMap() const { return ordered_index_sorted_task_expiry_map_ ;} const TaskConf::OrderedIndex_SortedTaskExpiryVector* TaskConf::FindSortedTaskExpiry(int64_t expiry) const { auto iter = ordered_index_sorted_task_expiry_map_.find(expiry); @@ -443,7 +577,7 @@ const protoconf::TaskConf::Task* TaskConf::FindFirstSortedTaskExpiry(int64_t exp } // OrderedIndex: (Expiry,ActivityID)@ActivityExpiry -const TaskConf::OrderedIndex_ActivityExpiryMap& TaskConf::FindActivityExpiry() const { return ordered_index_activity_expiry_map_ ;} +const TaskConf::OrderedIndex_ActivityExpiryMap& TaskConf::FindActivityExpiryMap() const { return ordered_index_activity_expiry_map_ ;} const TaskConf::OrderedIndex_ActivityExpiryVector* TaskConf::FindActivityExpiry(int64_t expiry, int64_t activity_id) const { auto iter = ordered_index_activity_expiry_map_.find({expiry, activity_id}); diff --git a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h index 7a9f2273..c6b68a68 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h @@ -53,14 +53,33 @@ class ActivityConf : public Messager { private: OrderedMap_ActivityMap ordered_map_; + // LevelIndex keys. + public: + struct LevelIndex_Activity_ChapterKey { + uint64_t activity_id; + uint32_t chapter_id; +#if __cplusplus >= 202002L + bool operator==(const LevelIndex_Activity_ChapterKey& other) const = default; +#else + bool operator==(const LevelIndex_Activity_ChapterKey& other) const { + return std::tie(activity_id, chapter_id) == std::tie(other.activity_id, other.chapter_id); + } +#endif + }; + struct LevelIndex_Activity_ChapterKeyHasher { + std::size_t operator()(const LevelIndex_Activity_ChapterKey& key) const { + return util::SugaredHashCombine(key.activity_id, key.chapter_id); + } + }; + // Index accessers. // Index: ActivityName public: using Index_ActivityVector = std::vector; using Index_ActivityMap = std::unordered_map; - // Finds the index (ActivityName) to value (Index_ActivityVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_ActivityMap& FindActivity() const; + // Finds the index: key(ActivityName) to value(Index_ActivityVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_ActivityMap& FindActivityMap() const; // Finds a vector of all values of the given key(s). const Index_ActivityVector* FindActivity(const std::string& activity_name) const; // Finds the first value of the given key(s). @@ -73,47 +92,82 @@ class ActivityConf : public Messager { public: using Index_ChapterVector = std::vector; using Index_ChapterMap = std::unordered_map; - // Finds the index (ChapterID) to value (Index_ChapterVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_ChapterMap& FindChapter() const; + // Finds the index: key(ChapterID) to value(Index_ChapterVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_ChapterMap& FindChapterMap() const; // Finds a vector of all values of the given key(s). const Index_ChapterVector* FindChapter(uint32_t chapter_id) const; // Finds the first value of the given key(s). const protoconf::ActivityConf::Activity::Chapter* FindFirstChapter(uint32_t chapter_id) const; + // Finds the index: key(ChapterID) to value(Index_ChapterVector), + // which is the upper 1st-level hashmap specified by (activity_id). + // One key may correspond to multiple values, which are represented by a vector. + const Index_ChapterMap* FindChapterMap(uint64_t activity_id) const; + // Finds a vector of all values of the given key(s) in the upper 1st-level hashmap specified by (activity_id). + const Index_ChapterVector* FindChapter(uint64_t activity_id, uint32_t chapter_id) const; + // Finds the first value of the given key(s) in the upper 1st-level hashmap specified by (activity_id). + const protoconf::ActivityConf::Activity::Chapter* FindFirstChapter(uint64_t activity_id, uint32_t chapter_id) const; private: Index_ChapterMap index_chapter_map_; + std::unordered_map index_chapter_map1_; // Index: ChapterName@NamedChapter public: using Index_NamedChapterVector = std::vector; using Index_NamedChapterMap = std::unordered_map; - // Finds the index (ChapterName@NamedChapter) to value (Index_NamedChapterVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_NamedChapterMap& FindNamedChapter() const; + // Finds the index: key(ChapterName@NamedChapter) to value(Index_NamedChapterVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_NamedChapterMap& FindNamedChapterMap() const; // Finds a vector of all values of the given key(s). const Index_NamedChapterVector* FindNamedChapter(const std::string& chapter_name) const; // Finds the first value of the given key(s). const protoconf::ActivityConf::Activity::Chapter* FindFirstNamedChapter(const std::string& chapter_name) const; + // Finds the index: key(ChapterName@NamedChapter) to value(Index_NamedChapterVector), + // which is the upper 1st-level hashmap specified by (activity_id). + // One key may correspond to multiple values, which are represented by a vector. + const Index_NamedChapterMap* FindNamedChapterMap(uint64_t activity_id) const; + // Finds a vector of all values of the given key(s) in the upper 1st-level hashmap specified by (activity_id). + const Index_NamedChapterVector* FindNamedChapter(uint64_t activity_id, const std::string& chapter_name) const; + // Finds the first value of the given key(s) in the upper 1st-level hashmap specified by (activity_id). + const protoconf::ActivityConf::Activity::Chapter* FindFirstNamedChapter(uint64_t activity_id, const std::string& chapter_name) const; private: Index_NamedChapterMap index_named_chapter_map_; + std::unordered_map index_named_chapter_map1_; // Index: SectionItemID@Award public: using Index_AwardVector = std::vector; using Index_AwardMap = std::unordered_map; - // Finds the index (SectionItemID@Award) to value (Index_AwardVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_AwardMap& FindAward() const; + // Finds the index: key(SectionItemID@Award) to value(Index_AwardVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_AwardMap& FindAwardMap() const; // Finds a vector of all values of the given key(s). const Index_AwardVector* FindAward(uint32_t id) const; // Finds the first value of the given key(s). const protoconf::Section::SectionItem* FindFirstAward(uint32_t id) const; + // Finds the index: key(SectionItemID@Award) to value(Index_AwardVector), + // which is the upper 1st-level hashmap specified by (activity_id). + // One key may correspond to multiple values, which are represented by a vector. + const Index_AwardMap* FindAwardMap(uint64_t activity_id) const; + // Finds a vector of all values of the given key(s) in the upper 1st-level hashmap specified by (activity_id). + const Index_AwardVector* FindAward(uint64_t activity_id, uint32_t id) const; + // Finds the first value of the given key(s) in the upper 1st-level hashmap specified by (activity_id). + const protoconf::Section::SectionItem* FindFirstAward(uint64_t activity_id, uint32_t id) const; + // Finds the index: key(SectionItemID@Award) to value(Index_AwardVector), + // which is the upper 2nd-level hashmap specified by (activity_id, chapter_id). + // One key may correspond to multiple values, which are represented by a vector. + const Index_AwardMap* FindAwardMap(uint64_t activity_id, uint32_t chapter_id) const; + // Finds a vector of all values of the given key(s) in the upper 2nd-level hashmap specified by (activity_id, chapter_id). + const Index_AwardVector* FindAward(uint64_t activity_id, uint32_t chapter_id, uint32_t id) const; + // Finds the first value of the given key(s) in the upper 2nd-level hashmap specified by (activity_id, chapter_id). + const protoconf::Section::SectionItem* FindFirstAward(uint64_t activity_id, uint32_t chapter_id, uint32_t id) const; private: Index_AwardMap index_award_map_; - + std::unordered_map index_award_map1_; + std::unordered_map index_award_map2_; }; class ChapterConf : public Messager { @@ -169,9 +223,9 @@ class TaskConf : public Messager { public: using Index_TaskVector = std::vector; using Index_TaskMap = std::unordered_map; - // Finds the index (ActivityID) to value (Index_TaskVector) hash map. - // One key may correspond to multiple values, which are contained by a vector. - const Index_TaskMap& FindTask() const; + // Finds the index: key(ActivityID) to value(Index_TaskVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_TaskMap& FindTaskMap() const; // Finds a vector of all values of the given key(s). const Index_TaskVector* FindTask(int64_t activity_id) const; // Finds the first value of the given key(s). @@ -185,9 +239,9 @@ class TaskConf : public Messager { public: using OrderedIndex_OrderedTaskVector = std::vector; using OrderedIndex_OrderedTaskMap = std::map; - // Finds the ordered index (Goal@OrderedTask) to value (OrderedIndex_OrderedTaskVector) map. - // One key may correspond to multiple values, which are contained by a vector. - const OrderedIndex_OrderedTaskMap& FindOrderedTask() const; + // Finds the ordered index: key(Goal@OrderedTask) to value(OrderedIndex_OrderedTaskVector) map. + // One key may correspond to multiple values, which are represented by a vector. + const OrderedIndex_OrderedTaskMap& FindOrderedTaskMap() const; // Finds a vector of all values of the given key(s). const OrderedIndex_OrderedTaskVector* FindOrderedTask(int64_t goal) const; // Finds the first value of the given key(s). @@ -200,9 +254,9 @@ class TaskConf : public Messager { public: using OrderedIndex_TaskExpiryVector = std::vector; using OrderedIndex_TaskExpiryMap = std::map; - // Finds the ordered index (Expiry@TaskExpiry) to value (OrderedIndex_TaskExpiryVector) map. - // One key may correspond to multiple values, which are contained by a vector. - const OrderedIndex_TaskExpiryMap& FindTaskExpiry() const; + // Finds the ordered index: key(Expiry@TaskExpiry) to value(OrderedIndex_TaskExpiryVector) map. + // One key may correspond to multiple values, which are represented by a vector. + const OrderedIndex_TaskExpiryMap& FindTaskExpiryMap() const; // Finds a vector of all values of the given key(s). const OrderedIndex_TaskExpiryVector* FindTaskExpiry(int64_t expiry) const; // Finds the first value of the given key(s). @@ -215,9 +269,9 @@ class TaskConf : public Messager { public: using OrderedIndex_SortedTaskExpiryVector = std::vector; using OrderedIndex_SortedTaskExpiryMap = std::map; - // Finds the ordered index (Expiry@SortedTaskExpiry) to value (OrderedIndex_SortedTaskExpiryVector) map. - // One key may correspond to multiple values, which are contained by a vector. - const OrderedIndex_SortedTaskExpiryMap& FindSortedTaskExpiry() const; + // Finds the ordered index: key(Expiry@SortedTaskExpiry) to value(OrderedIndex_SortedTaskExpiryVector) map. + // One key may correspond to multiple values, which are represented by a vector. + const OrderedIndex_SortedTaskExpiryMap& FindSortedTaskExpiryMap() const; // Finds a vector of all values of the given key(s). const OrderedIndex_SortedTaskExpiryVector* FindSortedTaskExpiry(int64_t expiry) const; // Finds the first value of the given key(s). @@ -241,9 +295,9 @@ class TaskConf : public Messager { }; using OrderedIndex_ActivityExpiryVector = std::vector; using OrderedIndex_ActivityExpiryMap = std::map; - // Finds the ordered index ((Expiry,ActivityID)@ActivityExpiry) to value (OrderedIndex_ActivityExpiryVector) map. - // One key may correspond to multiple values, which are contained by a vector. - const OrderedIndex_ActivityExpiryMap& FindActivityExpiry() const; + // Finds the ordered index: key((Expiry,ActivityID)@ActivityExpiry) to value(OrderedIndex_ActivityExpiryVector) map. + // One key may correspond to multiple values, which are represented by a vector. + const OrderedIndex_ActivityExpiryMap& FindActivityExpiryMap() const; // Finds a vector of all values of the given key(s). const OrderedIndex_ActivityExpiryVector* FindActivityExpiry(int64_t expiry, int64_t activity_id) const; // Finds the first value of the given key(s). @@ -251,7 +305,6 @@ class TaskConf : public Messager { private: OrderedIndex_ActivityExpiryMap ordered_index_activity_expiry_map_; - }; } // namespace tableau diff --git a/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go b/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go index a27aa011..3697f0b4 100644 --- a/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go @@ -34,6 +34,7 @@ type HeroConf struct { UnimplementedMessager data, originalData *protoconf.HeroConf indexAttrMap HeroConf_Index_AttrMap + indexAttrMap1 map[string]HeroConf_Index_AttrMap } // Name returns the HeroConf's message name. @@ -49,7 +50,7 @@ func (x *HeroConf) Data() *protoconf.HeroConf { return nil } -// Load fills HeroConf's inner message from file in the specified directory and format. +// Load loads HeroConf's content in the given dir, based on format and messager options. func (x *HeroConf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { start := time.Now() defer func() { @@ -66,7 +67,7 @@ func (x *HeroConf) Load(dir string, format format.Format, opts *load.MessagerOpt return x.processAfterLoad() } -// Store writes HeroConf's inner message to file in the specified directory and format. +// Store stores HeroConf's content to file in the specified directory and format. // Available formats: JSON, Bin, and Text. func (x *HeroConf) Store(dir string, format format.Format, options ...store.Option) error { return store.Store(x.Data(), dir, format, options...) @@ -94,19 +95,26 @@ func (x *HeroConf) originalMessage() proto.Message { func (x *HeroConf) processAfterLoad() error { // Index init. x.indexAttrMap = make(HeroConf_Index_AttrMap) - for _, item1 := range x.data.GetHeroMap() { - for _, item2 := range item1.GetAttrMap() { + x.indexAttrMap1 = make(map[string]HeroConf_Index_AttrMap) + for k1, v1 := range x.data.GetHeroMap() { + _ = k1 + for k2, v2 := range v1.GetAttrMap() { + _ = k2 { // Index: Title - key := item2.GetTitle() - x.indexAttrMap[key] = append(x.indexAttrMap[key], item2) + key := v2.GetTitle() + x.indexAttrMap[key] = append(x.indexAttrMap[key], v2) + if x.indexAttrMap1[k1] == nil { + x.indexAttrMap1[k1] = make(HeroConf_Index_AttrMap) + } + x.indexAttrMap1[k1][key] = append(x.indexAttrMap1[k1][key], v2) } } } return nil } -// Get1 finds value in the 1-level map. It will return +// Get1 finds value in the 1st-level map. It will return // NotFound error if the key is not found. func (x *HeroConf) Get1(name string) (*protoconf.HeroConf_Hero, error) { d := x.Data().GetHeroMap() @@ -117,7 +125,7 @@ func (x *HeroConf) Get1(name string) (*protoconf.HeroConf_Hero, error) { } } -// Get2 finds value in the 2-level map. It will return +// Get2 finds value in the 2nd-level map. It will return // NotFound error if the key is not found. func (x *HeroConf) Get2(name string, title string) (*protoconf.HeroConf_Hero_Attr, error) { conf, err := x.Get1(name) @@ -134,18 +142,18 @@ func (x *HeroConf) Get2(name string, title string) (*protoconf.HeroConf_Hero_Att // Index: Title -// FindAttrMap finds the index (Title) to value (protoconf.HeroConf_Hero_Attr) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindAttrMap finds the index: key(Title) to value(protoconf.HeroConf_Hero_Attr) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *HeroConf) FindAttrMap() HeroConf_Index_AttrMap { return x.indexAttrMap } -// FindAttr finds a slice of all values of the given key. +// FindAttr finds a slice of all values of the given key(s). func (x *HeroConf) FindAttr(title string) []*protoconf.HeroConf_Hero_Attr { return x.indexAttrMap[title] } -// FindFirstAttr finds the first value of the given key, +// FindFirstAttr finds the first value of the given key(s), // or nil if no value found. func (x *HeroConf) FindFirstAttr(title string) *protoconf.HeroConf_Hero_Attr { val := x.FindAttr(title) @@ -155,6 +163,29 @@ func (x *HeroConf) FindFirstAttr(title string) *protoconf.HeroConf_Hero_Attr { return nil } +// FindAttrMap1 finds the index: key(Title) to value(protoconf.HeroConf_Hero_Attr), +// which is the upper 1st-level map specified by (name). +// One key may correspond to multiple values, which are represented by a slice. +func (x *HeroConf) FindAttrMap1(name string) HeroConf_Index_AttrMap { + return x.indexAttrMap1[name] +} + +// FindAttr1 finds a slice of all values of the given key(s) in the upper 1st-level map +// specified by (name). +func (x *HeroConf) FindAttr1(name string, title string) []*protoconf.HeroConf_Hero_Attr { + return x.FindAttrMap1(name)[title] +} + +// FindFirstAttr1 finds the first value of the given key(s) in the upper 1st-level map +// specified by (name), or nil if no value found. +func (x *HeroConf) FindFirstAttr1(name string, title string) *protoconf.HeroConf_Hero_Attr { + val := x.FindAttr1(name, title) + if len(val) > 0 { + return val[0] + } + return nil +} + // OrderedMap types. type HeroBaseConf_OrderedMap_base_ItemMap = treemap.TreeMap[string, *base.Item] @@ -187,7 +218,7 @@ func (x *HeroBaseConf) Data() *protoconf.HeroBaseConf { return nil } -// Load fills HeroBaseConf's inner message from file in the specified directory and format. +// Load loads HeroBaseConf's content in the given dir, based on format and messager options. func (x *HeroBaseConf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { start := time.Now() defer func() { @@ -204,7 +235,7 @@ func (x *HeroBaseConf) Load(dir string, format format.Format, opts *load.Message return x.processAfterLoad() } -// Store writes HeroBaseConf's inner message to file in the specified directory and format. +// Store stores HeroBaseConf's content to file in the specified directory and format. // Available formats: JSON, Bin, and Text. func (x *HeroBaseConf) Store(dir string, format format.Format, options ...store.Option) error { return store.Store(x.Data(), dir, format, options...) @@ -247,7 +278,7 @@ func (x *HeroBaseConf) processAfterLoad() error { return nil } -// Get1 finds value in the 1-level map. It will return +// Get1 finds value in the 1st-level map. It will return // NotFound error if the key is not found. func (x *HeroBaseConf) Get1(name string) (*base.Hero, error) { d := x.Data().GetHeroMap() @@ -258,7 +289,7 @@ func (x *HeroBaseConf) Get1(name string) (*base.Hero, error) { } } -// Get2 finds value in the 2-level map. It will return +// Get2 finds value in the 2nd-level map. It will return // NotFound error if the key is not found. func (x *HeroBaseConf) Get2(name string, id string) (*base.Item, error) { conf, err := x.Get1(name) @@ -273,12 +304,12 @@ func (x *HeroBaseConf) Get2(name string, id string) (*base.Item, error) { } } -// GetOrderedMap returns the 1-level ordered map. +// GetOrderedMap returns the 1st-level ordered map. func (x *HeroBaseConf) GetOrderedMap() *HeroBaseConf_OrderedMap_base_HeroMap { return x.orderedMap } -// GetOrderedMap1 finds value in the 1-level ordered map. It will return +// GetOrderedMap1 finds value in the 1st-level ordered map. It will return // NotFound error if the key is not found. func (x *HeroBaseConf) GetOrderedMap1(name string) (*HeroBaseConf_OrderedMap_base_ItemMap, error) { conf := x.orderedMap diff --git a/test/go-tableau-loader/protoconf/loader/hub.pc.go b/test/go-tableau-loader/protoconf/loader/hub.pc.go index ab33e062..39e845f3 100644 --- a/test/go-tableau-loader/protoconf/loader/hub.pc.go +++ b/test/go-tableau-loader/protoconf/loader/hub.pc.go @@ -220,6 +220,18 @@ func (h *Hub) GetHeroBaseConf() *HeroBaseConf { return h.mc.Load().GetHeroBaseConf() } +func (h *Hub) GetFruitConf() *FruitConf { + return h.mc.Load().GetFruitConf() +} + +func (h *Hub) GetFruit2Conf() *Fruit2Conf { + return h.mc.Load().GetFruit2Conf() +} + +func (h *Hub) GetFruit3Conf() *Fruit3Conf { + return h.mc.Load().GetFruit3Conf() +} + func (h *Hub) GetItemConf() *ItemConf { return h.mc.Load().GetItemConf() } diff --git a/test/go-tableau-loader/protoconf/loader/index_conf.pc.go b/test/go-tableau-loader/protoconf/loader/index_conf.pc.go new file mode 100644 index 00000000..b645bc1d --- /dev/null +++ b/test/go-tableau-loader/protoconf/loader/index_conf.pc.go @@ -0,0 +1,701 @@ +// Code generated by protoc-gen-go-tableau-loader. DO NOT EDIT. +// versions: +// - protoc-gen-go-tableau-loader v0.10.1 +// - protoc v3.19.3 +// source: index_conf.proto + +package loader + +import ( + fmt "fmt" + treemap "github.com/tableauio/loader/pkg/treemap" + protoconf "github.com/tableauio/loader/test/go-tableau-loader/protoconf" + format "github.com/tableauio/tableau/format" + load "github.com/tableauio/tableau/load" + store "github.com/tableauio/tableau/store" + proto "google.golang.org/protobuf/proto" + sort "sort" + time "time" +) + +// OrderedIndex types. +// OrderedIndex: Price +type FruitConf_OrderedIndex_ItemMap = treemap.TreeMap[int32, []*protoconf.FruitConf_Fruit_Item] + +// FruitConf is a wrapper around protobuf message: protoconf.FruitConf. +// +// It is designed for three goals: +// +// 1. Easy use: simple yet powerful accessers. +// 2. Elegant API: concise and clean functions. +// 3. Extensibility: Map, OrdererdMap, Index, OrderedIndex... +type FruitConf struct { + UnimplementedMessager + data, originalData *protoconf.FruitConf + orderedIndexItemMap *FruitConf_OrderedIndex_ItemMap + orderedIndexItemMap1 map[int32]*FruitConf_OrderedIndex_ItemMap +} + +// Name returns the FruitConf's message name. +func (x *FruitConf) Name() string { + return string((*protoconf.FruitConf)(nil).ProtoReflect().Descriptor().Name()) +} + +// Data returns the FruitConf's inner message data. +func (x *FruitConf) Data() *protoconf.FruitConf { + if x != nil { + return x.data + } + return nil +} + +// Load loads FruitConf's content in the given dir, based on format and messager options. +func (x *FruitConf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { + start := time.Now() + defer func() { + x.Stats.Duration = time.Since(start) + }() + x.data = &protoconf.FruitConf{} + err := load.LoadMessagerInDir(x.data, dir, format, opts) + if err != nil { + return err + } + if x.backup { + x.originalData = proto.Clone(x.data).(*protoconf.FruitConf) + } + return x.processAfterLoad() +} + +// Store stores FruitConf's content to file in the specified directory and format. +// Available formats: JSON, Bin, and Text. +func (x *FruitConf) Store(dir string, format format.Format, options ...store.Option) error { + return store.Store(x.Data(), dir, format, options...) +} + +// Message returns the FruitConf's inner message data. +func (x *FruitConf) Message() proto.Message { + return x.Data() +} + +// Messager returns the current messager. +func (x *FruitConf) Messager() Messager { + return x +} + +// originalMessage returns the FruitConf's original inner message. +func (x *FruitConf) originalMessage() proto.Message { + if x != nil { + return x.originalData + } + return nil +} + +// processAfterLoad runs after this messager is loaded. +func (x *FruitConf) processAfterLoad() error { + // OrderedIndex init. + x.orderedIndexItemMap = treemap.New[int32, []*protoconf.FruitConf_Fruit_Item]() + x.orderedIndexItemMap1 = make(map[int32]*FruitConf_OrderedIndex_ItemMap) + for k1, v1 := range x.data.GetFruitMap() { + _ = k1 + for k2, v2 := range v1.GetItemMap() { + _ = k2 + { + // OrderedIndex: Price + key := v2.GetPrice() + value, _ := x.orderedIndexItemMap.Get(key) + x.orderedIndexItemMap.Put(key, append(value, v2)) + if x.orderedIndexItemMap1[k1] == nil { + x.orderedIndexItemMap1[k1] = treemap.New[int32, []*protoconf.FruitConf_Fruit_Item]() + } + orderedIndexItemMap1Value, _ := x.orderedIndexItemMap1[k1].Get(key) + x.orderedIndexItemMap1[k1].Put(key, append(orderedIndexItemMap1Value, v2)) + } + } + } + // OrderedIndex(sort): Price + orderedIndexItemMapSorter := func(itemList []*protoconf.FruitConf_Fruit_Item) func(i, j int) bool { + return func(i, j int) bool { + return itemList[i].GetId() < itemList[j].GetId() + } + } + x.orderedIndexItemMap.Range(func(key int32, itemList []*protoconf.FruitConf_Fruit_Item) bool { + sort.Slice(itemList, orderedIndexItemMapSorter(itemList)) + return true + }) + for _, itemMap := range x.orderedIndexItemMap1 { + itemMap.Range(func(key int32, itemList []*protoconf.FruitConf_Fruit_Item) bool { + sort.Slice(itemList, orderedIndexItemMapSorter(itemList)) + return true + }) + } + return nil +} + +// Get1 finds value in the 1st-level map. It will return +// NotFound error if the key is not found. +func (x *FruitConf) Get1(fruitType int32) (*protoconf.FruitConf_Fruit, error) { + d := x.Data().GetFruitMap() + if val, ok := d[fruitType]; !ok { + return nil, fmt.Errorf("fruitType(%v) %w", fruitType, ErrNotFound) + } else { + return val, nil + } +} + +// Get2 finds value in the 2nd-level map. It will return +// NotFound error if the key is not found. +func (x *FruitConf) Get2(fruitType int32, id int32) (*protoconf.FruitConf_Fruit_Item, error) { + conf, err := x.Get1(fruitType) + if err != nil { + return nil, err + } + d := conf.GetItemMap() + if val, ok := d[id]; !ok { + return nil, fmt.Errorf("id(%v) %w", id, ErrNotFound) + } else { + return val, nil + } +} + +// OrderedIndex: Price + +// FindItemMap finds the ordered index: key(Price) to value(protoconf.FruitConf_Fruit_Item) treemap. +// One key may correspond to multiple values, which are represented by a slice. +func (x *FruitConf) FindItemMap() *FruitConf_OrderedIndex_ItemMap { + return x.orderedIndexItemMap +} + +// FindItem finds a slice of all values of the given key(s). +func (x *FruitConf) FindItem(price int32) []*protoconf.FruitConf_Fruit_Item { + val, _ := x.orderedIndexItemMap.Get(price) + return val +} + +// FindFirstItem finds the first value of the given key(s), +// or nil if no value found. +func (x *FruitConf) FindFirstItem(price int32) *protoconf.FruitConf_Fruit_Item { + val := x.FindItem(price) + if len(val) > 0 { + return val[0] + } + return nil +} + +// FindItemMap1 finds the index: key(Price) to value(protoconf.FruitConf_Fruit_Item), +// which is the upper 1st-level treemap specified by (fruitType). +// One key may correspond to multiple values, which are represented by a slice. +func (x *FruitConf) FindItemMap1(fruitType int32) *FruitConf_OrderedIndex_ItemMap { + return x.orderedIndexItemMap1[fruitType] +} + +// FindItem1 finds a slice of all values of the given key(s) in the upper 1st-level treemap +// specified by (fruitType). +func (x *FruitConf) FindItem1(fruitType int32, price int32) []*protoconf.FruitConf_Fruit_Item { + val, _ := x.FindItemMap1(fruitType).Get(price) + return val +} + +// FindFirstItem1 finds the first value of the given key(s) in the upper 1st-level treemap +// specified by (fruitType), or nil if no value found. +func (x *FruitConf) FindFirstItem1(fruitType int32, price int32) *protoconf.FruitConf_Fruit_Item { + val := x.FindItem1(fruitType, price) + if len(val) > 0 { + return val[0] + } + return nil +} + +// Index types. +// Index: CountryName +type Fruit2Conf_Index_CountryMap = map[string][]*protoconf.Fruit2Conf_Fruit_Country + +// Index: CountryItemAttrName +type Fruit2Conf_Index_AttrMap = map[string][]*protoconf.Fruit2Conf_Fruit_Country_Item_Attr + +// OrderedIndex types. +// OrderedIndex: CountryItemPrice +type Fruit2Conf_OrderedIndex_ItemMap = treemap.TreeMap[int32, []*protoconf.Fruit2Conf_Fruit_Country_Item] + +// Fruit2Conf is a wrapper around protobuf message: protoconf.Fruit2Conf. +// +// It is designed for three goals: +// +// 1. Easy use: simple yet powerful accessers. +// 2. Elegant API: concise and clean functions. +// 3. Extensibility: Map, OrdererdMap, Index, OrderedIndex... +type Fruit2Conf struct { + UnimplementedMessager + data, originalData *protoconf.Fruit2Conf + indexCountryMap Fruit2Conf_Index_CountryMap + indexAttrMap Fruit2Conf_Index_AttrMap + indexAttrMap1 map[int32]Fruit2Conf_Index_AttrMap + orderedIndexItemMap *Fruit2Conf_OrderedIndex_ItemMap + orderedIndexItemMap1 map[int32]*Fruit2Conf_OrderedIndex_ItemMap +} + +// Name returns the Fruit2Conf's message name. +func (x *Fruit2Conf) Name() string { + return string((*protoconf.Fruit2Conf)(nil).ProtoReflect().Descriptor().Name()) +} + +// Data returns the Fruit2Conf's inner message data. +func (x *Fruit2Conf) Data() *protoconf.Fruit2Conf { + if x != nil { + return x.data + } + return nil +} + +// Load loads Fruit2Conf's content in the given dir, based on format and messager options. +func (x *Fruit2Conf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { + start := time.Now() + defer func() { + x.Stats.Duration = time.Since(start) + }() + x.data = &protoconf.Fruit2Conf{} + err := load.LoadMessagerInDir(x.data, dir, format, opts) + if err != nil { + return err + } + if x.backup { + x.originalData = proto.Clone(x.data).(*protoconf.Fruit2Conf) + } + return x.processAfterLoad() +} + +// Store stores Fruit2Conf's content to file in the specified directory and format. +// Available formats: JSON, Bin, and Text. +func (x *Fruit2Conf) Store(dir string, format format.Format, options ...store.Option) error { + return store.Store(x.Data(), dir, format, options...) +} + +// Message returns the Fruit2Conf's inner message data. +func (x *Fruit2Conf) Message() proto.Message { + return x.Data() +} + +// Messager returns the current messager. +func (x *Fruit2Conf) Messager() Messager { + return x +} + +// originalMessage returns the Fruit2Conf's original inner message. +func (x *Fruit2Conf) originalMessage() proto.Message { + if x != nil { + return x.originalData + } + return nil +} + +// processAfterLoad runs after this messager is loaded. +func (x *Fruit2Conf) processAfterLoad() error { + // Index init. + x.indexCountryMap = make(Fruit2Conf_Index_CountryMap) + x.indexAttrMap = make(Fruit2Conf_Index_AttrMap) + x.indexAttrMap1 = make(map[int32]Fruit2Conf_Index_AttrMap) + for k1, v1 := range x.data.GetFruitMap() { + _ = k1 + for _, v2 := range v1.GetCountryList() { + { + // Index: CountryName + key := v2.GetName() + x.indexCountryMap[key] = append(x.indexCountryMap[key], v2) + } + for k2, v3 := range v2.GetItemMap() { + _ = k2 + for _, v4 := range v3.GetAttrList() { + { + // Index: CountryItemAttrName + key := v4.GetName() + x.indexAttrMap[key] = append(x.indexAttrMap[key], v4) + if x.indexAttrMap1[k1] == nil { + x.indexAttrMap1[k1] = make(Fruit2Conf_Index_AttrMap) + } + x.indexAttrMap1[k1][key] = append(x.indexAttrMap1[k1][key], v4) + } + } + } + } + } + // OrderedIndex init. + x.orderedIndexItemMap = treemap.New[int32, []*protoconf.Fruit2Conf_Fruit_Country_Item]() + x.orderedIndexItemMap1 = make(map[int32]*Fruit2Conf_OrderedIndex_ItemMap) + for k1, v1 := range x.data.GetFruitMap() { + _ = k1 + for _, v2 := range v1.GetCountryList() { + for k2, v3 := range v2.GetItemMap() { + _ = k2 + { + // OrderedIndex: CountryItemPrice + key := v3.GetPrice() + value, _ := x.orderedIndexItemMap.Get(key) + x.orderedIndexItemMap.Put(key, append(value, v3)) + if x.orderedIndexItemMap1[k1] == nil { + x.orderedIndexItemMap1[k1] = treemap.New[int32, []*protoconf.Fruit2Conf_Fruit_Country_Item]() + } + orderedIndexItemMap1Value, _ := x.orderedIndexItemMap1[k1].Get(key) + x.orderedIndexItemMap1[k1].Put(key, append(orderedIndexItemMap1Value, v3)) + } + } + } + } + // OrderedIndex(sort): CountryItemPrice + orderedIndexItemMapSorter := func(itemList []*protoconf.Fruit2Conf_Fruit_Country_Item) func(i, j int) bool { + return func(i, j int) bool { + return itemList[i].GetId() < itemList[j].GetId() + } + } + x.orderedIndexItemMap.Range(func(key int32, itemList []*protoconf.Fruit2Conf_Fruit_Country_Item) bool { + sort.Slice(itemList, orderedIndexItemMapSorter(itemList)) + return true + }) + for _, itemMap := range x.orderedIndexItemMap1 { + itemMap.Range(func(key int32, itemList []*protoconf.Fruit2Conf_Fruit_Country_Item) bool { + sort.Slice(itemList, orderedIndexItemMapSorter(itemList)) + return true + }) + } + return nil +} + +// Get1 finds value in the 1st-level map. It will return +// NotFound error if the key is not found. +func (x *Fruit2Conf) Get1(fruitType int32) (*protoconf.Fruit2Conf_Fruit, error) { + d := x.Data().GetFruitMap() + if val, ok := d[fruitType]; !ok { + return nil, fmt.Errorf("fruitType(%v) %w", fruitType, ErrNotFound) + } else { + return val, nil + } +} + +// Index: CountryName + +// FindCountryMap finds the index: key(CountryName) to value(protoconf.Fruit2Conf_Fruit_Country) map. +// One key may correspond to multiple values, which are represented by a slice. +func (x *Fruit2Conf) FindCountryMap() Fruit2Conf_Index_CountryMap { + return x.indexCountryMap +} + +// FindCountry finds a slice of all values of the given key(s). +func (x *Fruit2Conf) FindCountry(name string) []*protoconf.Fruit2Conf_Fruit_Country { + return x.indexCountryMap[name] +} + +// FindFirstCountry finds the first value of the given key(s), +// or nil if no value found. +func (x *Fruit2Conf) FindFirstCountry(name string) *protoconf.Fruit2Conf_Fruit_Country { + val := x.FindCountry(name) + if len(val) > 0 { + return val[0] + } + return nil +} + +// Index: CountryItemAttrName + +// FindAttrMap finds the index: key(CountryItemAttrName) to value(protoconf.Fruit2Conf_Fruit_Country_Item_Attr) map. +// One key may correspond to multiple values, which are represented by a slice. +func (x *Fruit2Conf) FindAttrMap() Fruit2Conf_Index_AttrMap { + return x.indexAttrMap +} + +// FindAttr finds a slice of all values of the given key(s). +func (x *Fruit2Conf) FindAttr(name string) []*protoconf.Fruit2Conf_Fruit_Country_Item_Attr { + return x.indexAttrMap[name] +} + +// FindFirstAttr finds the first value of the given key(s), +// or nil if no value found. +func (x *Fruit2Conf) FindFirstAttr(name string) *protoconf.Fruit2Conf_Fruit_Country_Item_Attr { + val := x.FindAttr(name) + if len(val) > 0 { + return val[0] + } + return nil +} + +// FindAttrMap1 finds the index: key(CountryItemAttrName) to value(protoconf.Fruit2Conf_Fruit_Country_Item_Attr), +// which is the upper 1st-level map specified by (fruitType). +// One key may correspond to multiple values, which are represented by a slice. +func (x *Fruit2Conf) FindAttrMap1(fruitType int32) Fruit2Conf_Index_AttrMap { + return x.indexAttrMap1[fruitType] +} + +// FindAttr1 finds a slice of all values of the given key(s) in the upper 1st-level map +// specified by (fruitType). +func (x *Fruit2Conf) FindAttr1(fruitType int32, name string) []*protoconf.Fruit2Conf_Fruit_Country_Item_Attr { + return x.FindAttrMap1(fruitType)[name] +} + +// FindFirstAttr1 finds the first value of the given key(s) in the upper 1st-level map +// specified by (fruitType), or nil if no value found. +func (x *Fruit2Conf) FindFirstAttr1(fruitType int32, name string) *protoconf.Fruit2Conf_Fruit_Country_Item_Attr { + val := x.FindAttr1(fruitType, name) + if len(val) > 0 { + return val[0] + } + return nil +} + +// OrderedIndex: CountryItemPrice + +// FindItemMap finds the ordered index: key(CountryItemPrice) to value(protoconf.Fruit2Conf_Fruit_Country_Item) treemap. +// One key may correspond to multiple values, which are represented by a slice. +func (x *Fruit2Conf) FindItemMap() *Fruit2Conf_OrderedIndex_ItemMap { + return x.orderedIndexItemMap +} + +// FindItem finds a slice of all values of the given key(s). +func (x *Fruit2Conf) FindItem(price int32) []*protoconf.Fruit2Conf_Fruit_Country_Item { + val, _ := x.orderedIndexItemMap.Get(price) + return val +} + +// FindFirstItem finds the first value of the given key(s), +// or nil if no value found. +func (x *Fruit2Conf) FindFirstItem(price int32) *protoconf.Fruit2Conf_Fruit_Country_Item { + val := x.FindItem(price) + if len(val) > 0 { + return val[0] + } + return nil +} + +// FindItemMap1 finds the index: key(CountryItemPrice) to value(protoconf.Fruit2Conf_Fruit_Country_Item), +// which is the upper 1st-level treemap specified by (fruitType). +// One key may correspond to multiple values, which are represented by a slice. +func (x *Fruit2Conf) FindItemMap1(fruitType int32) *Fruit2Conf_OrderedIndex_ItemMap { + return x.orderedIndexItemMap1[fruitType] +} + +// FindItem1 finds a slice of all values of the given key(s) in the upper 1st-level treemap +// specified by (fruitType). +func (x *Fruit2Conf) FindItem1(fruitType int32, price int32) []*protoconf.Fruit2Conf_Fruit_Country_Item { + val, _ := x.FindItemMap1(fruitType).Get(price) + return val +} + +// FindFirstItem1 finds the first value of the given key(s) in the upper 1st-level treemap +// specified by (fruitType), or nil if no value found. +func (x *Fruit2Conf) FindFirstItem1(fruitType int32, price int32) *protoconf.Fruit2Conf_Fruit_Country_Item { + val := x.FindItem1(fruitType, price) + if len(val) > 0 { + return val[0] + } + return nil +} + +// Index types. +// Index: CountryName +type Fruit3Conf_Index_CountryMap = map[string][]*protoconf.Fruit3Conf_Fruit_Country + +// Index: CountryItemAttrName +type Fruit3Conf_Index_AttrMap = map[string][]*protoconf.Fruit3Conf_Fruit_Country_Item_Attr + +// OrderedIndex types. +// OrderedIndex: CountryItemPrice +type Fruit3Conf_OrderedIndex_ItemMap = treemap.TreeMap[int32, []*protoconf.Fruit3Conf_Fruit_Country_Item] + +// Fruit3Conf is a wrapper around protobuf message: protoconf.Fruit3Conf. +// +// It is designed for three goals: +// +// 1. Easy use: simple yet powerful accessers. +// 2. Elegant API: concise and clean functions. +// 3. Extensibility: Map, OrdererdMap, Index, OrderedIndex... +type Fruit3Conf struct { + UnimplementedMessager + data, originalData *protoconf.Fruit3Conf + indexCountryMap Fruit3Conf_Index_CountryMap + indexAttrMap Fruit3Conf_Index_AttrMap + orderedIndexItemMap *Fruit3Conf_OrderedIndex_ItemMap +} + +// Name returns the Fruit3Conf's message name. +func (x *Fruit3Conf) Name() string { + return string((*protoconf.Fruit3Conf)(nil).ProtoReflect().Descriptor().Name()) +} + +// Data returns the Fruit3Conf's inner message data. +func (x *Fruit3Conf) Data() *protoconf.Fruit3Conf { + if x != nil { + return x.data + } + return nil +} + +// Load loads Fruit3Conf's content in the given dir, based on format and messager options. +func (x *Fruit3Conf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { + start := time.Now() + defer func() { + x.Stats.Duration = time.Since(start) + }() + x.data = &protoconf.Fruit3Conf{} + err := load.LoadMessagerInDir(x.data, dir, format, opts) + if err != nil { + return err + } + if x.backup { + x.originalData = proto.Clone(x.data).(*protoconf.Fruit3Conf) + } + return x.processAfterLoad() +} + +// Store stores Fruit3Conf's content to file in the specified directory and format. +// Available formats: JSON, Bin, and Text. +func (x *Fruit3Conf) Store(dir string, format format.Format, options ...store.Option) error { + return store.Store(x.Data(), dir, format, options...) +} + +// Message returns the Fruit3Conf's inner message data. +func (x *Fruit3Conf) Message() proto.Message { + return x.Data() +} + +// Messager returns the current messager. +func (x *Fruit3Conf) Messager() Messager { + return x +} + +// originalMessage returns the Fruit3Conf's original inner message. +func (x *Fruit3Conf) originalMessage() proto.Message { + if x != nil { + return x.originalData + } + return nil +} + +// processAfterLoad runs after this messager is loaded. +func (x *Fruit3Conf) processAfterLoad() error { + // Index init. + x.indexCountryMap = make(Fruit3Conf_Index_CountryMap) + x.indexAttrMap = make(Fruit3Conf_Index_AttrMap) + for _, v1 := range x.data.GetFruitList() { + for _, v2 := range v1.GetCountryList() { + { + // Index: CountryName + key := v2.GetName() + x.indexCountryMap[key] = append(x.indexCountryMap[key], v2) + } + for k1, v3 := range v2.GetItemMap() { + _ = k1 + for _, v4 := range v3.GetAttrList() { + { + // Index: CountryItemAttrName + key := v4.GetName() + x.indexAttrMap[key] = append(x.indexAttrMap[key], v4) + } + } + } + } + } + // OrderedIndex init. + x.orderedIndexItemMap = treemap.New[int32, []*protoconf.Fruit3Conf_Fruit_Country_Item]() + for _, v1 := range x.data.GetFruitList() { + for _, v2 := range v1.GetCountryList() { + for k1, v3 := range v2.GetItemMap() { + _ = k1 + { + // OrderedIndex: CountryItemPrice + key := v3.GetPrice() + value, _ := x.orderedIndexItemMap.Get(key) + x.orderedIndexItemMap.Put(key, append(value, v3)) + } + } + } + } + // OrderedIndex(sort): CountryItemPrice + orderedIndexItemMapSorter := func(itemList []*protoconf.Fruit3Conf_Fruit_Country_Item) func(i, j int) bool { + return func(i, j int) bool { + return itemList[i].GetId() < itemList[j].GetId() + } + } + x.orderedIndexItemMap.Range(func(key int32, itemList []*protoconf.Fruit3Conf_Fruit_Country_Item) bool { + sort.Slice(itemList, orderedIndexItemMapSorter(itemList)) + return true + }) + return nil +} + +// Index: CountryName + +// FindCountryMap finds the index: key(CountryName) to value(protoconf.Fruit3Conf_Fruit_Country) map. +// One key may correspond to multiple values, which are represented by a slice. +func (x *Fruit3Conf) FindCountryMap() Fruit3Conf_Index_CountryMap { + return x.indexCountryMap +} + +// FindCountry finds a slice of all values of the given key(s). +func (x *Fruit3Conf) FindCountry(name string) []*protoconf.Fruit3Conf_Fruit_Country { + return x.indexCountryMap[name] +} + +// FindFirstCountry finds the first value of the given key(s), +// or nil if no value found. +func (x *Fruit3Conf) FindFirstCountry(name string) *protoconf.Fruit3Conf_Fruit_Country { + val := x.FindCountry(name) + if len(val) > 0 { + return val[0] + } + return nil +} + +// Index: CountryItemAttrName + +// FindAttrMap finds the index: key(CountryItemAttrName) to value(protoconf.Fruit3Conf_Fruit_Country_Item_Attr) map. +// One key may correspond to multiple values, which are represented by a slice. +func (x *Fruit3Conf) FindAttrMap() Fruit3Conf_Index_AttrMap { + return x.indexAttrMap +} + +// FindAttr finds a slice of all values of the given key(s). +func (x *Fruit3Conf) FindAttr(name string) []*protoconf.Fruit3Conf_Fruit_Country_Item_Attr { + return x.indexAttrMap[name] +} + +// FindFirstAttr finds the first value of the given key(s), +// or nil if no value found. +func (x *Fruit3Conf) FindFirstAttr(name string) *protoconf.Fruit3Conf_Fruit_Country_Item_Attr { + val := x.FindAttr(name) + if len(val) > 0 { + return val[0] + } + return nil +} + +// OrderedIndex: CountryItemPrice + +// FindItemMap finds the ordered index: key(CountryItemPrice) to value(protoconf.Fruit3Conf_Fruit_Country_Item) treemap. +// One key may correspond to multiple values, which are represented by a slice. +func (x *Fruit3Conf) FindItemMap() *Fruit3Conf_OrderedIndex_ItemMap { + return x.orderedIndexItemMap +} + +// FindItem finds a slice of all values of the given key(s). +func (x *Fruit3Conf) FindItem(price int32) []*protoconf.Fruit3Conf_Fruit_Country_Item { + val, _ := x.orderedIndexItemMap.Get(price) + return val +} + +// FindFirstItem finds the first value of the given key(s), +// or nil if no value found. +func (x *Fruit3Conf) FindFirstItem(price int32) *protoconf.Fruit3Conf_Fruit_Country_Item { + val := x.FindItem(price) + if len(val) > 0 { + return val[0] + } + return nil +} + +func init() { + Register(func() Messager { + return new(FruitConf) + }) + Register(func() Messager { + return new(Fruit2Conf) + }) + Register(func() Messager { + return new(Fruit3Conf) + }) +} diff --git a/test/go-tableau-loader/protoconf/loader/item_conf.pc.go b/test/go-tableau-loader/protoconf/loader/item_conf.pc.go index e658b08e..c675f43e 100644 --- a/test/go-tableau-loader/protoconf/loader/item_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/item_conf.pc.go @@ -119,7 +119,7 @@ func (x *ItemConf) Data() *protoconf.ItemConf { return nil } -// Load fills ItemConf's inner message from file in the specified directory and format. +// Load loads ItemConf's content in the given dir, based on format and messager options. func (x *ItemConf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { start := time.Now() defer func() { @@ -136,7 +136,7 @@ func (x *ItemConf) Load(dir string, format format.Format, opts *load.MessagerOpt return x.processAfterLoad() } -// Store writes ItemConf's inner message to file in the specified directory and format. +// Store stores ItemConf's content to file in the specified directory and format. // Available formats: JSON, Bin, and Text. func (x *ItemConf) Store(dir string, format format.Format, options ...store.Option) error { return store.Store(x.Data(), dir, format, options...) @@ -179,117 +179,128 @@ func (x *ItemConf) processAfterLoad() error { x.indexItemPathNameMap = make(ItemConf_Index_ItemPathNameMap) x.indexItemPathFriendIdMap = make(ItemConf_Index_ItemPathFriendIDMap) x.indexUseEffectTypeMap = make(ItemConf_Index_UseEffectTypeMap) - for _, item1 := range x.data.GetItemMap() { + for k1, v1 := range x.data.GetItemMap() { + _ = k1 { // Index: Type - key := item1.GetType() - x.indexItemMap[key] = append(x.indexItemMap[key], item1) + key := v1.GetType() + x.indexItemMap[key] = append(x.indexItemMap[key], v1) } { // Index: Param@ItemInfo - for _, item2 := range item1.GetParamList() { - key := item2 - x.indexItemInfoMap[key] = append(x.indexItemInfoMap[key], item1) + for _, v2 := range v1.GetParamList() { + key := v2 + x.indexItemInfoMap[key] = append(x.indexItemInfoMap[key], v1) } } { // Index: Default@ItemDefaultInfo - key := item1.GetDefault() - x.indexItemDefaultInfoMap[key] = append(x.indexItemDefaultInfoMap[key], item1) + key := v1.GetDefault() + x.indexItemDefaultInfoMap[key] = append(x.indexItemDefaultInfoMap[key], v1) } { // Index: ExtType@ItemExtInfo - for _, item2 := range item1.GetExtTypeList() { - key := item2 - x.indexItemExtInfoMap[key] = append(x.indexItemExtInfoMap[key], item1) + for _, v2 := range v1.GetExtTypeList() { + key := v2 + x.indexItemExtInfoMap[key] = append(x.indexItemExtInfoMap[key], v1) } } { // Index: (ID,Name)@AwardItem - key := ItemConf_Index_AwardItemKey{item1.GetId(), item1.GetName()} - x.indexAwardItemMap[key] = append(x.indexAwardItemMap[key], item1) + key := ItemConf_Index_AwardItemKey{v1.GetId(), v1.GetName()} + x.indexAwardItemMap[key] = append(x.indexAwardItemMap[key], v1) } { // Index: (ID,Type,Param,ExtType)@SpecialItem - for _, indexItem2 := range item1.GetParamList() { - for _, indexItem3 := range item1.GetExtTypeList() { - key := ItemConf_Index_SpecialItemKey{item1.GetId(), item1.GetType(), indexItem2, indexItem3} - x.indexSpecialItemMap[key] = append(x.indexSpecialItemMap[key], item1) + for _, indexItem2 := range v1.GetParamList() { + for _, indexItem3 := range v1.GetExtTypeList() { + key := ItemConf_Index_SpecialItemKey{v1.GetId(), v1.GetType(), indexItem2, indexItem3} + x.indexSpecialItemMap[key] = append(x.indexSpecialItemMap[key], v1) } } } { // Index: PathDir@ItemPathDir - key := item1.GetPath().GetDir() - x.indexItemPathDirMap[key] = append(x.indexItemPathDirMap[key], item1) + key := v1.GetPath().GetDir() + x.indexItemPathDirMap[key] = append(x.indexItemPathDirMap[key], v1) } { // Index: PathName@ItemPathName - for _, item2 := range item1.GetPath().GetNameList() { - key := item2 - x.indexItemPathNameMap[key] = append(x.indexItemPathNameMap[key], item1) + for _, v2 := range v1.GetPath().GetNameList() { + key := v2 + x.indexItemPathNameMap[key] = append(x.indexItemPathNameMap[key], v1) } } { // Index: PathFriendID@ItemPathFriendID - key := item1.GetPath().GetFriend().GetId() - x.indexItemPathFriendIdMap[key] = append(x.indexItemPathFriendIdMap[key], item1) + key := v1.GetPath().GetFriend().GetId() + x.indexItemPathFriendIdMap[key] = append(x.indexItemPathFriendIdMap[key], v1) } { // Index: UseEffectType@UseEffectType - key := item1.GetUseEffect().GetType() - x.indexUseEffectTypeMap[key] = append(x.indexUseEffectTypeMap[key], item1) + key := v1.GetUseEffect().GetType() + x.indexUseEffectTypeMap[key] = append(x.indexUseEffectTypeMap[key], v1) } } // Index(sort): Param@ItemInfo - for _, item := range x.indexItemInfoMap { - sort.Slice(item, func(i, j int) bool { - return item[i].GetId() < item[j].GetId() - }) + indexItemInfoMapSorter := func(itemList []*protoconf.ItemConf_Item) func(i, j int) bool { + return func(i, j int) bool { + return itemList[i].GetId() < itemList[j].GetId() + } + } + for _, itemList := range x.indexItemInfoMap { + sort.Slice(itemList, indexItemInfoMapSorter(itemList)) } // Index(sort): (ID,Name)@AwardItem - for _, item := range x.indexAwardItemMap { - sort.Slice(item, func(i, j int) bool { - if item[i].GetType() != item[j].GetType() { - return item[i].GetType() < item[j].GetType() + indexAwardItemMapSorter := func(itemList []*protoconf.ItemConf_Item) func(i, j int) bool { + return func(i, j int) bool { + if itemList[i].GetType() != itemList[j].GetType() { + return itemList[i].GetType() < itemList[j].GetType() } - return item[i].GetUseEffect().GetType() < item[j].GetUseEffect().GetType() - }) + return itemList[i].GetUseEffect().GetType() < itemList[j].GetUseEffect().GetType() + } + } + for _, itemList := range x.indexAwardItemMap { + sort.Slice(itemList, indexAwardItemMapSorter(itemList)) } // OrderedIndex init. x.orderedIndexExtTypeMap = treemap.New[protoconf.FruitType, []*protoconf.ItemConf_Item]() x.orderedIndexParamExtTypeMap = treemap.New2[ItemConf_OrderedIndex_ParamExtTypeKey, []*protoconf.ItemConf_Item]() - for _, item1 := range x.data.GetItemMap() { + for k1, v1 := range x.data.GetItemMap() { + _ = k1 { // OrderedIndex: ExtType@ExtType - for _, item2 := range item1.GetExtTypeList() { - key := item2 + for _, v2 := range v1.GetExtTypeList() { + key := v2 value, _ := x.orderedIndexExtTypeMap.Get(key) - x.orderedIndexExtTypeMap.Put(key, append(value, item1)) + x.orderedIndexExtTypeMap.Put(key, append(value, v1)) } } { // OrderedIndex: (Param,ExtType)@ParamExtType - for _, indexItem0 := range item1.GetParamList() { - for _, indexItem1 := range item1.GetExtTypeList() { + for _, indexItem0 := range v1.GetParamList() { + for _, indexItem1 := range v1.GetExtTypeList() { key := ItemConf_OrderedIndex_ParamExtTypeKey{indexItem0, indexItem1} value, _ := x.orderedIndexParamExtTypeMap.Get(key) - x.orderedIndexParamExtTypeMap.Put(key, append(value, item1)) + x.orderedIndexParamExtTypeMap.Put(key, append(value, v1)) } } } } // OrderedIndex(sort): (Param,ExtType)@ParamExtType - x.orderedIndexParamExtTypeMap.Range(func(key ItemConf_OrderedIndex_ParamExtTypeKey, item []*protoconf.ItemConf_Item) bool { - sort.Slice(item, func(i, j int) bool { - return item[i].GetId() < item[j].GetId() - }) + orderedIndexParamExtTypeMapSorter := func(itemList []*protoconf.ItemConf_Item) func(i, j int) bool { + return func(i, j int) bool { + return itemList[i].GetId() < itemList[j].GetId() + } + } + x.orderedIndexParamExtTypeMap.Range(func(key ItemConf_OrderedIndex_ParamExtTypeKey, itemList []*protoconf.ItemConf_Item) bool { + sort.Slice(itemList, orderedIndexParamExtTypeMapSorter(itemList)) return true }) return nil } -// Get1 finds value in the 1-level map. It will return +// Get1 finds value in the 1st-level map. It will return // NotFound error if the key is not found. func (x *ItemConf) Get1(id uint32) (*protoconf.ItemConf_Item, error) { d := x.Data().GetItemMap() @@ -300,25 +311,25 @@ func (x *ItemConf) Get1(id uint32) (*protoconf.ItemConf_Item, error) { } } -// GetOrderedMap returns the 1-level ordered map. +// GetOrderedMap returns the 1st-level ordered map. func (x *ItemConf) GetOrderedMap() *ItemConf_OrderedMap_ItemMap { return x.orderedMap } // Index: Type -// FindItemMap finds the index (Type) to value (protoconf.ItemConf_Item) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindItemMap finds the index: key(Type) to value(protoconf.ItemConf_Item) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindItemMap() ItemConf_Index_ItemMap { return x.indexItemMap } -// FindItem finds a slice of all values of the given key. +// FindItem finds a slice of all values of the given key(s). func (x *ItemConf) FindItem(type_ protoconf.FruitType) []*protoconf.ItemConf_Item { return x.indexItemMap[type_] } -// FindFirstItem finds the first value of the given key, +// FindFirstItem finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstItem(type_ protoconf.FruitType) *protoconf.ItemConf_Item { val := x.FindItem(type_) @@ -330,18 +341,18 @@ func (x *ItemConf) FindFirstItem(type_ protoconf.FruitType) *protoconf.ItemConf_ // Index: Param@ItemInfo -// FindItemInfoMap finds the index (Param@ItemInfo) to value (protoconf.ItemConf_Item) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindItemInfoMap finds the index: key(Param@ItemInfo) to value(protoconf.ItemConf_Item) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindItemInfoMap() ItemConf_Index_ItemInfoMap { return x.indexItemInfoMap } -// FindItemInfo finds a slice of all values of the given key. +// FindItemInfo finds a slice of all values of the given key(s). func (x *ItemConf) FindItemInfo(param int32) []*protoconf.ItemConf_Item { return x.indexItemInfoMap[param] } -// FindFirstItemInfo finds the first value of the given key, +// FindFirstItemInfo finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstItemInfo(param int32) *protoconf.ItemConf_Item { val := x.FindItemInfo(param) @@ -353,18 +364,18 @@ func (x *ItemConf) FindFirstItemInfo(param int32) *protoconf.ItemConf_Item { // Index: Default@ItemDefaultInfo -// FindItemDefaultInfoMap finds the index (Default@ItemDefaultInfo) to value (protoconf.ItemConf_Item) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindItemDefaultInfoMap finds the index: key(Default@ItemDefaultInfo) to value(protoconf.ItemConf_Item) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindItemDefaultInfoMap() ItemConf_Index_ItemDefaultInfoMap { return x.indexItemDefaultInfoMap } -// FindItemDefaultInfo finds a slice of all values of the given key. +// FindItemDefaultInfo finds a slice of all values of the given key(s). func (x *ItemConf) FindItemDefaultInfo(default_ string) []*protoconf.ItemConf_Item { return x.indexItemDefaultInfoMap[default_] } -// FindFirstItemDefaultInfo finds the first value of the given key, +// FindFirstItemDefaultInfo finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstItemDefaultInfo(default_ string) *protoconf.ItemConf_Item { val := x.FindItemDefaultInfo(default_) @@ -376,18 +387,18 @@ func (x *ItemConf) FindFirstItemDefaultInfo(default_ string) *protoconf.ItemConf // Index: ExtType@ItemExtInfo -// FindItemExtInfoMap finds the index (ExtType@ItemExtInfo) to value (protoconf.ItemConf_Item) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindItemExtInfoMap finds the index: key(ExtType@ItemExtInfo) to value(protoconf.ItemConf_Item) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindItemExtInfoMap() ItemConf_Index_ItemExtInfoMap { return x.indexItemExtInfoMap } -// FindItemExtInfo finds a slice of all values of the given key. +// FindItemExtInfo finds a slice of all values of the given key(s). func (x *ItemConf) FindItemExtInfo(extType protoconf.FruitType) []*protoconf.ItemConf_Item { return x.indexItemExtInfoMap[extType] } -// FindFirstItemExtInfo finds the first value of the given key, +// FindFirstItemExtInfo finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstItemExtInfo(extType protoconf.FruitType) *protoconf.ItemConf_Item { val := x.FindItemExtInfo(extType) @@ -399,18 +410,18 @@ func (x *ItemConf) FindFirstItemExtInfo(extType protoconf.FruitType) *protoconf. // Index: (ID,Name)@AwardItem -// FindAwardItemMap finds the index ((ID,Name)@AwardItem) to value (protoconf.ItemConf_Item) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindAwardItemMap finds the index: key((ID,Name)@AwardItem) to value(protoconf.ItemConf_Item) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindAwardItemMap() ItemConf_Index_AwardItemMap { return x.indexAwardItemMap } -// FindAwardItem finds a slice of all values of the given key. +// FindAwardItem finds a slice of all values of the given key(s). func (x *ItemConf) FindAwardItem(id uint32, name string) []*protoconf.ItemConf_Item { return x.indexAwardItemMap[ItemConf_Index_AwardItemKey{id, name}] } -// FindFirstAwardItem finds the first value of the given key, +// FindFirstAwardItem finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstAwardItem(id uint32, name string) *protoconf.ItemConf_Item { val := x.FindAwardItem(id, name) @@ -422,18 +433,18 @@ func (x *ItemConf) FindFirstAwardItem(id uint32, name string) *protoconf.ItemCon // Index: (ID,Type,Param,ExtType)@SpecialItem -// FindSpecialItemMap finds the index ((ID,Type,Param,ExtType)@SpecialItem) to value (protoconf.ItemConf_Item) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindSpecialItemMap finds the index: key((ID,Type,Param,ExtType)@SpecialItem) to value(protoconf.ItemConf_Item) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindSpecialItemMap() ItemConf_Index_SpecialItemMap { return x.indexSpecialItemMap } -// FindSpecialItem finds a slice of all values of the given key. +// FindSpecialItem finds a slice of all values of the given key(s). func (x *ItemConf) FindSpecialItem(id uint32, type_ protoconf.FruitType, param int32, extType protoconf.FruitType) []*protoconf.ItemConf_Item { return x.indexSpecialItemMap[ItemConf_Index_SpecialItemKey{id, type_, param, extType}] } -// FindFirstSpecialItem finds the first value of the given key, +// FindFirstSpecialItem finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstSpecialItem(id uint32, type_ protoconf.FruitType, param int32, extType protoconf.FruitType) *protoconf.ItemConf_Item { val := x.FindSpecialItem(id, type_, param, extType) @@ -445,18 +456,18 @@ func (x *ItemConf) FindFirstSpecialItem(id uint32, type_ protoconf.FruitType, pa // Index: PathDir@ItemPathDir -// FindItemPathDirMap finds the index (PathDir@ItemPathDir) to value (protoconf.ItemConf_Item) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindItemPathDirMap finds the index: key(PathDir@ItemPathDir) to value(protoconf.ItemConf_Item) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindItemPathDirMap() ItemConf_Index_ItemPathDirMap { return x.indexItemPathDirMap } -// FindItemPathDir finds a slice of all values of the given key. +// FindItemPathDir finds a slice of all values of the given key(s). func (x *ItemConf) FindItemPathDir(dir string) []*protoconf.ItemConf_Item { return x.indexItemPathDirMap[dir] } -// FindFirstItemPathDir finds the first value of the given key, +// FindFirstItemPathDir finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstItemPathDir(dir string) *protoconf.ItemConf_Item { val := x.FindItemPathDir(dir) @@ -468,18 +479,18 @@ func (x *ItemConf) FindFirstItemPathDir(dir string) *protoconf.ItemConf_Item { // Index: PathName@ItemPathName -// FindItemPathNameMap finds the index (PathName@ItemPathName) to value (protoconf.ItemConf_Item) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindItemPathNameMap finds the index: key(PathName@ItemPathName) to value(protoconf.ItemConf_Item) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindItemPathNameMap() ItemConf_Index_ItemPathNameMap { return x.indexItemPathNameMap } -// FindItemPathName finds a slice of all values of the given key. +// FindItemPathName finds a slice of all values of the given key(s). func (x *ItemConf) FindItemPathName(name string) []*protoconf.ItemConf_Item { return x.indexItemPathNameMap[name] } -// FindFirstItemPathName finds the first value of the given key, +// FindFirstItemPathName finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstItemPathName(name string) *protoconf.ItemConf_Item { val := x.FindItemPathName(name) @@ -491,18 +502,18 @@ func (x *ItemConf) FindFirstItemPathName(name string) *protoconf.ItemConf_Item { // Index: PathFriendID@ItemPathFriendID -// FindItemPathFriendIDMap finds the index (PathFriendID@ItemPathFriendID) to value (protoconf.ItemConf_Item) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindItemPathFriendIDMap finds the index: key(PathFriendID@ItemPathFriendID) to value(protoconf.ItemConf_Item) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindItemPathFriendIDMap() ItemConf_Index_ItemPathFriendIDMap { return x.indexItemPathFriendIdMap } -// FindItemPathFriendID finds a slice of all values of the given key. +// FindItemPathFriendID finds a slice of all values of the given key(s). func (x *ItemConf) FindItemPathFriendID(id uint32) []*protoconf.ItemConf_Item { return x.indexItemPathFriendIdMap[id] } -// FindFirstItemPathFriendID finds the first value of the given key, +// FindFirstItemPathFriendID finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstItemPathFriendID(id uint32) *protoconf.ItemConf_Item { val := x.FindItemPathFriendID(id) @@ -514,18 +525,18 @@ func (x *ItemConf) FindFirstItemPathFriendID(id uint32) *protoconf.ItemConf_Item // Index: UseEffectType@UseEffectType -// FindUseEffectTypeMap finds the index (UseEffectType@UseEffectType) to value (protoconf.ItemConf_Item) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindUseEffectTypeMap finds the index: key(UseEffectType@UseEffectType) to value(protoconf.ItemConf_Item) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindUseEffectTypeMap() ItemConf_Index_UseEffectTypeMap { return x.indexUseEffectTypeMap } -// FindUseEffectType finds a slice of all values of the given key. +// FindUseEffectType finds a slice of all values of the given key(s). func (x *ItemConf) FindUseEffectType(type_ protoconf.UseEffect_Type) []*protoconf.ItemConf_Item { return x.indexUseEffectTypeMap[type_] } -// FindFirstUseEffectType finds the first value of the given key, +// FindFirstUseEffectType finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstUseEffectType(type_ protoconf.UseEffect_Type) *protoconf.ItemConf_Item { val := x.FindUseEffectType(type_) @@ -537,19 +548,19 @@ func (x *ItemConf) FindFirstUseEffectType(type_ protoconf.UseEffect_Type) *proto // OrderedIndex: ExtType@ExtType -// FindExtTypeMap finds the ordered index (ExtType@ExtType) to value (protoconf.ItemConf_Item) treemap. -// One key may correspond to multiple values, which are contained by a slice. +// FindExtTypeMap finds the ordered index: key(ExtType@ExtType) to value(protoconf.ItemConf_Item) treemap. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindExtTypeMap() *ItemConf_OrderedIndex_ExtTypeMap { return x.orderedIndexExtTypeMap } -// FindExtType finds a slice of all values of the given key. +// FindExtType finds a slice of all values of the given key(s). func (x *ItemConf) FindExtType(extType protoconf.FruitType) []*protoconf.ItemConf_Item { val, _ := x.orderedIndexExtTypeMap.Get(extType) return val } -// FindFirstExtType finds the first value of the given key, +// FindFirstExtType finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstExtType(extType protoconf.FruitType) *protoconf.ItemConf_Item { val := x.FindExtType(extType) @@ -561,19 +572,19 @@ func (x *ItemConf) FindFirstExtType(extType protoconf.FruitType) *protoconf.Item // OrderedIndex: (Param,ExtType)@ParamExtType -// FindParamExtTypeMap finds the ordered index ((Param,ExtType)@ParamExtType) to value (protoconf.ItemConf_Item) treemap. -// One key may correspond to multiple values, which are contained by a slice. +// FindParamExtTypeMap finds the ordered index: key((Param,ExtType)@ParamExtType) to value(protoconf.ItemConf_Item) treemap. +// One key may correspond to multiple values, which are represented by a slice. func (x *ItemConf) FindParamExtTypeMap() *ItemConf_OrderedIndex_ParamExtTypeMap { return x.orderedIndexParamExtTypeMap } -// FindParamExtType finds a slice of all values of the given key. +// FindParamExtType finds a slice of all values of the given key(s). func (x *ItemConf) FindParamExtType(param int32, extType protoconf.FruitType) []*protoconf.ItemConf_Item { val, _ := x.orderedIndexParamExtTypeMap.Get(ItemConf_OrderedIndex_ParamExtTypeKey{param, extType}) return val } -// FindFirstParamExtType finds the first value of the given key, +// FindFirstParamExtType finds the first value of the given key(s), // or nil if no value found. func (x *ItemConf) FindFirstParamExtType(param int32, extType protoconf.FruitType) *protoconf.ItemConf_Item { val := x.FindParamExtType(param, extType) diff --git a/test/go-tableau-loader/protoconf/loader/messager_container.pc.go b/test/go-tableau-loader/protoconf/loader/messager_container.pc.go index 39cf846b..e95289d3 100644 --- a/test/go-tableau-loader/protoconf/loader/messager_container.pc.go +++ b/test/go-tableau-loader/protoconf/loader/messager_container.pc.go @@ -15,6 +15,9 @@ type MessagerContainer struct { // all messagers as fields for fast access heroConf *HeroConf heroBaseConf *HeroBaseConf + fruitConf *FruitConf + fruit2Conf *Fruit2Conf + fruit3Conf *Fruit3Conf itemConf *ItemConf patchReplaceConf *PatchReplaceConf patchMergeConf *PatchMergeConf @@ -31,6 +34,9 @@ func newMessagerContainer(messagerMap MessagerMap) *MessagerContainer { loadedTime: time.Now(), heroConf: GetMessager[*HeroConf](messagerMap), heroBaseConf: GetMessager[*HeroBaseConf](messagerMap), + fruitConf: GetMessager[*FruitConf](messagerMap), + fruit2Conf: GetMessager[*Fruit2Conf](messagerMap), + fruit3Conf: GetMessager[*Fruit3Conf](messagerMap), itemConf: GetMessager[*ItemConf](messagerMap), patchReplaceConf: GetMessager[*PatchReplaceConf](messagerMap), patchMergeConf: GetMessager[*PatchMergeConf](messagerMap), @@ -64,6 +70,18 @@ func (mc *MessagerContainer) GetHeroBaseConf() *HeroBaseConf { return mc.heroBaseConf } +func (mc *MessagerContainer) GetFruitConf() *FruitConf { + return mc.fruitConf +} + +func (mc *MessagerContainer) GetFruit2Conf() *Fruit2Conf { + return mc.fruit2Conf +} + +func (mc *MessagerContainer) GetFruit3Conf() *Fruit3Conf { + return mc.fruit3Conf +} + func (mc *MessagerContainer) GetItemConf() *ItemConf { return mc.itemConf } diff --git a/test/go-tableau-loader/protoconf/loader/patch_conf.pc.go b/test/go-tableau-loader/protoconf/loader/patch_conf.pc.go index 9b1d4c4f..b4a5a278 100644 --- a/test/go-tableau-loader/protoconf/loader/patch_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/patch_conf.pc.go @@ -41,7 +41,7 @@ func (x *PatchReplaceConf) Data() *protoconf.PatchReplaceConf { return nil } -// Load fills PatchReplaceConf's inner message from file in the specified directory and format. +// Load loads PatchReplaceConf's content in the given dir, based on format and messager options. func (x *PatchReplaceConf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { start := time.Now() defer func() { @@ -58,7 +58,7 @@ func (x *PatchReplaceConf) Load(dir string, format format.Format, opts *load.Mes return x.processAfterLoad() } -// Store writes PatchReplaceConf's inner message to file in the specified directory and format. +// Store stores PatchReplaceConf's content to file in the specified directory and format. // Available formats: JSON, Bin, and Text. func (x *PatchReplaceConf) Store(dir string, format format.Format, options ...store.Option) error { return store.Store(x.Data(), dir, format, options...) @@ -107,7 +107,7 @@ func (x *PatchMergeConf) Data() *protoconf.PatchMergeConf { return nil } -// Load fills PatchMergeConf's inner message from file in the specified directory and format. +// Load loads PatchMergeConf's content in the given dir, based on format and messager options. func (x *PatchMergeConf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { start := time.Now() defer func() { @@ -124,7 +124,7 @@ func (x *PatchMergeConf) Load(dir string, format format.Format, opts *load.Messa return x.processAfterLoad() } -// Store writes PatchMergeConf's inner message to file in the specified directory and format. +// Store stores PatchMergeConf's content to file in the specified directory and format. // Available formats: JSON, Bin, and Text. func (x *PatchMergeConf) Store(dir string, format format.Format, options ...store.Option) error { return store.Store(x.Data(), dir, format, options...) @@ -148,7 +148,7 @@ func (x *PatchMergeConf) originalMessage() proto.Message { return nil } -// Get1 finds value in the 1-level map. It will return +// Get1 finds value in the 1st-level map. It will return // NotFound error if the key is not found. func (x *PatchMergeConf) Get1(id uint32) (*protoconf.Item, error) { d := x.Data().GetItemMap() @@ -184,7 +184,7 @@ func (x *RecursivePatchConf) Data() *protoconf.RecursivePatchConf { return nil } -// Load fills RecursivePatchConf's inner message from file in the specified directory and format. +// Load loads RecursivePatchConf's content in the given dir, based on format and messager options. func (x *RecursivePatchConf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { start := time.Now() defer func() { @@ -201,7 +201,7 @@ func (x *RecursivePatchConf) Load(dir string, format format.Format, opts *load.M return x.processAfterLoad() } -// Store writes RecursivePatchConf's inner message to file in the specified directory and format. +// Store stores RecursivePatchConf's content to file in the specified directory and format. // Available formats: JSON, Bin, and Text. func (x *RecursivePatchConf) Store(dir string, format format.Format, options ...store.Option) error { return store.Store(x.Data(), dir, format, options...) @@ -225,7 +225,7 @@ func (x *RecursivePatchConf) originalMessage() proto.Message { return nil } -// Get1 finds value in the 1-level map. It will return +// Get1 finds value in the 1st-level map. It will return // NotFound error if the key is not found. func (x *RecursivePatchConf) Get1(shopId uint32) (*protoconf.RecursivePatchConf_Shop, error) { d := x.Data().GetShopMap() @@ -236,7 +236,7 @@ func (x *RecursivePatchConf) Get1(shopId uint32) (*protoconf.RecursivePatchConf_ } } -// Get2 finds value in the 2-level map. It will return +// Get2 finds value in the 2nd-level map. It will return // NotFound error if the key is not found. func (x *RecursivePatchConf) Get2(shopId uint32, goodsId uint32) (*protoconf.RecursivePatchConf_Shop_Goods, error) { conf, err := x.Get1(shopId) @@ -251,7 +251,7 @@ func (x *RecursivePatchConf) Get2(shopId uint32, goodsId uint32) (*protoconf.Rec } } -// Get3 finds value in the 3-level map. It will return +// Get3 finds value in the 3rd-level map. It will return // NotFound error if the key is not found. func (x *RecursivePatchConf) Get3(shopId uint32, goodsId uint32, type_ uint32) (*protoconf.RecursivePatchConf_Shop_Goods_Currency, error) { conf, err := x.Get2(shopId, goodsId) @@ -266,7 +266,7 @@ func (x *RecursivePatchConf) Get3(shopId uint32, goodsId uint32, type_ uint32) ( } } -// Get4 finds value in the 4-level map. It will return +// Get4 finds value in the 4th-level map. It will return // NotFound error if the key is not found. func (x *RecursivePatchConf) Get4(shopId uint32, goodsId uint32, type_ uint32, key4 int32) (int32, error) { conf, err := x.Get3(shopId, goodsId, type_) diff --git a/test/go-tableau-loader/protoconf/loader/test_conf.pc.go b/test/go-tableau-loader/protoconf/loader/test_conf.pc.go index f17d6e07..254bb6e1 100644 --- a/test/go-tableau-loader/protoconf/loader/test_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/test_conf.pc.go @@ -31,6 +31,12 @@ type ActivityConf_OrderedMap_Activity_ChapterMap = treemap.TreeMap[uint32, *Acti type ActivityConf_OrderedMap_ActivityValue = pair.Pair[*ActivityConf_OrderedMap_Activity_ChapterMap, *protoconf.ActivityConf_Activity] type ActivityConf_OrderedMap_ActivityMap = treemap.TreeMap[uint64, *ActivityConf_OrderedMap_ActivityValue] +// LevelIndex keys. +type ActivityConf_LevelIndex_Activity_ChapterKey struct { + ActivityId uint64 + ChapterId uint32 +} + // Index types. // Index: ActivityName type ActivityConf_Index_ActivityMap = map[string][]*protoconf.ActivityConf_Activity @@ -53,12 +59,16 @@ type ActivityConf_Index_AwardMap = map[uint32][]*protoconf.Section_SectionItem // 3. Extensibility: Map, OrdererdMap, Index, OrderedIndex... type ActivityConf struct { UnimplementedMessager - data, originalData *protoconf.ActivityConf - orderedMap *ActivityConf_OrderedMap_ActivityMap - indexActivityMap ActivityConf_Index_ActivityMap - indexChapterMap ActivityConf_Index_ChapterMap - indexNamedChapterMap ActivityConf_Index_NamedChapterMap - indexAwardMap ActivityConf_Index_AwardMap + data, originalData *protoconf.ActivityConf + orderedMap *ActivityConf_OrderedMap_ActivityMap + indexActivityMap ActivityConf_Index_ActivityMap + indexChapterMap ActivityConf_Index_ChapterMap + indexChapterMap1 map[uint64]ActivityConf_Index_ChapterMap + indexNamedChapterMap ActivityConf_Index_NamedChapterMap + indexNamedChapterMap1 map[uint64]ActivityConf_Index_NamedChapterMap + indexAwardMap ActivityConf_Index_AwardMap + indexAwardMap1 map[uint64]ActivityConf_Index_AwardMap + indexAwardMap2 map[ActivityConf_LevelIndex_Activity_ChapterKey]ActivityConf_Index_AwardMap } // Name returns the ActivityConf's message name. @@ -74,7 +84,7 @@ func (x *ActivityConf) Data() *protoconf.ActivityConf { return nil } -// Load fills ActivityConf's inner message from file in the specified directory and format. +// Load loads ActivityConf's content in the given dir, based on format and messager options. func (x *ActivityConf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { start := time.Now() defer func() { @@ -91,7 +101,7 @@ func (x *ActivityConf) Load(dir string, format format.Format, opts *load.Message return x.processAfterLoad() } -// Store writes ActivityConf's inner message to file in the specified directory and format. +// Store stores ActivityConf's content to file in the specified directory and format. // Available formats: JSON, Bin, and Text. func (x *ActivityConf) Store(dir string, format format.Format, options ...store.Option) error { return store.Store(x.Data(), dir, format, options...) @@ -150,46 +160,78 @@ func (x *ActivityConf) processAfterLoad() error { // Index init. x.indexActivityMap = make(ActivityConf_Index_ActivityMap) x.indexChapterMap = make(ActivityConf_Index_ChapterMap) + x.indexChapterMap1 = make(map[uint64]ActivityConf_Index_ChapterMap) x.indexNamedChapterMap = make(ActivityConf_Index_NamedChapterMap) + x.indexNamedChapterMap1 = make(map[uint64]ActivityConf_Index_NamedChapterMap) x.indexAwardMap = make(ActivityConf_Index_AwardMap) - for _, item1 := range x.data.GetActivityMap() { + x.indexAwardMap1 = make(map[uint64]ActivityConf_Index_AwardMap) + x.indexAwardMap2 = make(map[ActivityConf_LevelIndex_Activity_ChapterKey]ActivityConf_Index_AwardMap) + for k1, v1 := range x.data.GetActivityMap() { + _ = k1 { // Index: ActivityName - key := item1.GetActivityName() - x.indexActivityMap[key] = append(x.indexActivityMap[key], item1) + key := v1.GetActivityName() + x.indexActivityMap[key] = append(x.indexActivityMap[key], v1) } - for _, item2 := range item1.GetChapterMap() { + for k2, v2 := range v1.GetChapterMap() { + _ = k2 { // Index: ChapterID - key := item2.GetChapterId() - x.indexChapterMap[key] = append(x.indexChapterMap[key], item2) + key := v2.GetChapterId() + x.indexChapterMap[key] = append(x.indexChapterMap[key], v2) + if x.indexChapterMap1[k1] == nil { + x.indexChapterMap1[k1] = make(ActivityConf_Index_ChapterMap) + } + x.indexChapterMap1[k1][key] = append(x.indexChapterMap1[k1][key], v2) } { // Index: ChapterName@NamedChapter - key := item2.GetChapterName() - x.indexNamedChapterMap[key] = append(x.indexNamedChapterMap[key], item2) + key := v2.GetChapterName() + x.indexNamedChapterMap[key] = append(x.indexNamedChapterMap[key], v2) + if x.indexNamedChapterMap1[k1] == nil { + x.indexNamedChapterMap1[k1] = make(ActivityConf_Index_NamedChapterMap) + } + x.indexNamedChapterMap1[k1][key] = append(x.indexNamedChapterMap1[k1][key], v2) } - for _, item3 := range item2.GetSectionMap() { - for _, item4 := range item3.GetSectionItemList() { + for k3, v3 := range v2.GetSectionMap() { + _ = k3 + for _, v4 := range v3.GetSectionItemList() { { // Index: SectionItemID@Award - key := item4.GetId() - x.indexAwardMap[key] = append(x.indexAwardMap[key], item4) + key := v4.GetId() + x.indexAwardMap[key] = append(x.indexAwardMap[key], v4) + if x.indexAwardMap1[k1] == nil { + x.indexAwardMap1[k1] = make(ActivityConf_Index_AwardMap) + } + x.indexAwardMap1[k1][key] = append(x.indexAwardMap1[k1][key], v4) + indexAwardMap2Keys := ActivityConf_LevelIndex_Activity_ChapterKey{k1, k2} + if x.indexAwardMap2[indexAwardMap2Keys] == nil { + x.indexAwardMap2[indexAwardMap2Keys] = make(ActivityConf_Index_AwardMap) + } + x.indexAwardMap2[indexAwardMap2Keys][key] = append(x.indexAwardMap2[indexAwardMap2Keys][key], v4) } } } } } // Index(sort): ChapterName@NamedChapter - for _, item := range x.indexNamedChapterMap { - sort.Slice(item, func(i, j int) bool { - return item[i].GetAwardId() < item[j].GetAwardId() - }) + indexNamedChapterMapSorter := func(itemList []*protoconf.ActivityConf_Activity_Chapter) func(i, j int) bool { + return func(i, j int) bool { + return itemList[i].GetAwardId() < itemList[j].GetAwardId() + } + } + for _, itemList := range x.indexNamedChapterMap { + sort.Slice(itemList, indexNamedChapterMapSorter(itemList)) + } + for _, itemMap := range x.indexNamedChapterMap1 { + for _, itemList := range itemMap { + sort.Slice(itemList, indexNamedChapterMapSorter(itemList)) + } } return nil } -// Get1 finds value in the 1-level map. It will return +// Get1 finds value in the 1st-level map. It will return // NotFound error if the key is not found. func (x *ActivityConf) Get1(activityId uint64) (*protoconf.ActivityConf_Activity, error) { d := x.Data().GetActivityMap() @@ -200,7 +242,7 @@ func (x *ActivityConf) Get1(activityId uint64) (*protoconf.ActivityConf_Activity } } -// Get2 finds value in the 2-level map. It will return +// Get2 finds value in the 2nd-level map. It will return // NotFound error if the key is not found. func (x *ActivityConf) Get2(activityId uint64, chapterId uint32) (*protoconf.ActivityConf_Activity_Chapter, error) { conf, err := x.Get1(activityId) @@ -215,7 +257,7 @@ func (x *ActivityConf) Get2(activityId uint64, chapterId uint32) (*protoconf.Act } } -// Get3 finds value in the 3-level map. It will return +// Get3 finds value in the 3rd-level map. It will return // NotFound error if the key is not found. func (x *ActivityConf) Get3(activityId uint64, chapterId uint32, sectionId uint32) (*protoconf.Section, error) { conf, err := x.Get2(activityId, chapterId) @@ -230,7 +272,7 @@ func (x *ActivityConf) Get3(activityId uint64, chapterId uint32, sectionId uint3 } } -// Get4 finds value in the 4-level map. It will return +// Get4 finds value in the 4th-level map. It will return // NotFound error if the key is not found. func (x *ActivityConf) Get4(activityId uint64, chapterId uint32, sectionId uint32, key4 uint32) (int32, error) { conf, err := x.Get3(activityId, chapterId, sectionId) @@ -245,12 +287,12 @@ func (x *ActivityConf) Get4(activityId uint64, chapterId uint32, sectionId uint3 } } -// GetOrderedMap returns the 1-level ordered map. +// GetOrderedMap returns the 1st-level ordered map. func (x *ActivityConf) GetOrderedMap() *ActivityConf_OrderedMap_ActivityMap { return x.orderedMap } -// GetOrderedMap1 finds value in the 1-level ordered map. It will return +// GetOrderedMap1 finds value in the 1st-level ordered map. It will return // NotFound error if the key is not found. func (x *ActivityConf) GetOrderedMap1(activityId uint64) (*ActivityConf_OrderedMap_Activity_ChapterMap, error) { conf := x.orderedMap @@ -261,7 +303,7 @@ func (x *ActivityConf) GetOrderedMap1(activityId uint64) (*ActivityConf_OrderedM } } -// GetOrderedMap2 finds value in the 2-level ordered map. It will return +// GetOrderedMap2 finds value in the 2nd-level ordered map. It will return // NotFound error if the key is not found. func (x *ActivityConf) GetOrderedMap2(activityId uint64, chapterId uint32) (*ActivityConf_OrderedMap_protoconf_SectionMap, error) { conf, err := x.GetOrderedMap1(activityId) @@ -275,7 +317,7 @@ func (x *ActivityConf) GetOrderedMap2(activityId uint64, chapterId uint32) (*Act } } -// GetOrderedMap3 finds value in the 3-level ordered map. It will return +// GetOrderedMap3 finds value in the 3rd-level ordered map. It will return // NotFound error if the key is not found. func (x *ActivityConf) GetOrderedMap3(activityId uint64, chapterId uint32, sectionId uint32) (*ActivityConf_OrderedMap_int32Map, error) { conf, err := x.GetOrderedMap2(activityId, chapterId) @@ -291,18 +333,18 @@ func (x *ActivityConf) GetOrderedMap3(activityId uint64, chapterId uint32, secti // Index: ActivityName -// FindActivityMap finds the index (ActivityName) to value (protoconf.ActivityConf_Activity) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindActivityMap finds the index: key(ActivityName) to value(protoconf.ActivityConf_Activity) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ActivityConf) FindActivityMap() ActivityConf_Index_ActivityMap { return x.indexActivityMap } -// FindActivity finds a slice of all values of the given key. +// FindActivity finds a slice of all values of the given key(s). func (x *ActivityConf) FindActivity(activityName string) []*protoconf.ActivityConf_Activity { return x.indexActivityMap[activityName] } -// FindFirstActivity finds the first value of the given key, +// FindFirstActivity finds the first value of the given key(s), // or nil if no value found. func (x *ActivityConf) FindFirstActivity(activityName string) *protoconf.ActivityConf_Activity { val := x.FindActivity(activityName) @@ -314,18 +356,18 @@ func (x *ActivityConf) FindFirstActivity(activityName string) *protoconf.Activit // Index: ChapterID -// FindChapterMap finds the index (ChapterID) to value (protoconf.ActivityConf_Activity_Chapter) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindChapterMap finds the index: key(ChapterID) to value(protoconf.ActivityConf_Activity_Chapter) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ActivityConf) FindChapterMap() ActivityConf_Index_ChapterMap { return x.indexChapterMap } -// FindChapter finds a slice of all values of the given key. +// FindChapter finds a slice of all values of the given key(s). func (x *ActivityConf) FindChapter(chapterId uint32) []*protoconf.ActivityConf_Activity_Chapter { return x.indexChapterMap[chapterId] } -// FindFirstChapter finds the first value of the given key, +// FindFirstChapter finds the first value of the given key(s), // or nil if no value found. func (x *ActivityConf) FindFirstChapter(chapterId uint32) *protoconf.ActivityConf_Activity_Chapter { val := x.FindChapter(chapterId) @@ -335,20 +377,43 @@ func (x *ActivityConf) FindFirstChapter(chapterId uint32) *protoconf.ActivityCon return nil } +// FindChapterMap1 finds the index: key(ChapterID) to value(protoconf.ActivityConf_Activity_Chapter), +// which is the upper 1st-level map specified by (activityId). +// One key may correspond to multiple values, which are represented by a slice. +func (x *ActivityConf) FindChapterMap1(activityId uint64) ActivityConf_Index_ChapterMap { + return x.indexChapterMap1[activityId] +} + +// FindChapter1 finds a slice of all values of the given key(s) in the upper 1st-level map +// specified by (activityId). +func (x *ActivityConf) FindChapter1(activityId uint64, chapterId uint32) []*protoconf.ActivityConf_Activity_Chapter { + return x.FindChapterMap1(activityId)[chapterId] +} + +// FindFirstChapter1 finds the first value of the given key(s) in the upper 1st-level map +// specified by (activityId), or nil if no value found. +func (x *ActivityConf) FindFirstChapter1(activityId uint64, chapterId uint32) *protoconf.ActivityConf_Activity_Chapter { + val := x.FindChapter1(activityId, chapterId) + if len(val) > 0 { + return val[0] + } + return nil +} + // Index: ChapterName@NamedChapter -// FindNamedChapterMap finds the index (ChapterName@NamedChapter) to value (protoconf.ActivityConf_Activity_Chapter) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindNamedChapterMap finds the index: key(ChapterName@NamedChapter) to value(protoconf.ActivityConf_Activity_Chapter) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ActivityConf) FindNamedChapterMap() ActivityConf_Index_NamedChapterMap { return x.indexNamedChapterMap } -// FindNamedChapter finds a slice of all values of the given key. +// FindNamedChapter finds a slice of all values of the given key(s). func (x *ActivityConf) FindNamedChapter(chapterName string) []*protoconf.ActivityConf_Activity_Chapter { return x.indexNamedChapterMap[chapterName] } -// FindFirstNamedChapter finds the first value of the given key, +// FindFirstNamedChapter finds the first value of the given key(s), // or nil if no value found. func (x *ActivityConf) FindFirstNamedChapter(chapterName string) *protoconf.ActivityConf_Activity_Chapter { val := x.FindNamedChapter(chapterName) @@ -358,20 +423,43 @@ func (x *ActivityConf) FindFirstNamedChapter(chapterName string) *protoconf.Acti return nil } +// FindNamedChapterMap1 finds the index: key(ChapterName@NamedChapter) to value(protoconf.ActivityConf_Activity_Chapter), +// which is the upper 1st-level map specified by (activityId). +// One key may correspond to multiple values, which are represented by a slice. +func (x *ActivityConf) FindNamedChapterMap1(activityId uint64) ActivityConf_Index_NamedChapterMap { + return x.indexNamedChapterMap1[activityId] +} + +// FindNamedChapter1 finds a slice of all values of the given key(s) in the upper 1st-level map +// specified by (activityId). +func (x *ActivityConf) FindNamedChapter1(activityId uint64, chapterName string) []*protoconf.ActivityConf_Activity_Chapter { + return x.FindNamedChapterMap1(activityId)[chapterName] +} + +// FindFirstNamedChapter1 finds the first value of the given key(s) in the upper 1st-level map +// specified by (activityId), or nil if no value found. +func (x *ActivityConf) FindFirstNamedChapter1(activityId uint64, chapterName string) *protoconf.ActivityConf_Activity_Chapter { + val := x.FindNamedChapter1(activityId, chapterName) + if len(val) > 0 { + return val[0] + } + return nil +} + // Index: SectionItemID@Award -// FindAwardMap finds the index (SectionItemID@Award) to value (protoconf.Section_SectionItem) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindAwardMap finds the index: key(SectionItemID@Award) to value(protoconf.Section_SectionItem) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *ActivityConf) FindAwardMap() ActivityConf_Index_AwardMap { return x.indexAwardMap } -// FindAward finds a slice of all values of the given key. +// FindAward finds a slice of all values of the given key(s). func (x *ActivityConf) FindAward(id uint32) []*protoconf.Section_SectionItem { return x.indexAwardMap[id] } -// FindFirstAward finds the first value of the given key, +// FindFirstAward finds the first value of the given key(s), // or nil if no value found. func (x *ActivityConf) FindFirstAward(id uint32) *protoconf.Section_SectionItem { val := x.FindAward(id) @@ -381,6 +469,52 @@ func (x *ActivityConf) FindFirstAward(id uint32) *protoconf.Section_SectionItem return nil } +// FindAwardMap1 finds the index: key(SectionItemID@Award) to value(protoconf.Section_SectionItem), +// which is the upper 1st-level map specified by (activityId). +// One key may correspond to multiple values, which are represented by a slice. +func (x *ActivityConf) FindAwardMap1(activityId uint64) ActivityConf_Index_AwardMap { + return x.indexAwardMap1[activityId] +} + +// FindAward1 finds a slice of all values of the given key(s) in the upper 1st-level map +// specified by (activityId). +func (x *ActivityConf) FindAward1(activityId uint64, id uint32) []*protoconf.Section_SectionItem { + return x.FindAwardMap1(activityId)[id] +} + +// FindFirstAward1 finds the first value of the given key(s) in the upper 1st-level map +// specified by (activityId), or nil if no value found. +func (x *ActivityConf) FindFirstAward1(activityId uint64, id uint32) *protoconf.Section_SectionItem { + val := x.FindAward1(activityId, id) + if len(val) > 0 { + return val[0] + } + return nil +} + +// FindAwardMap2 finds the index: key(SectionItemID@Award) to value(protoconf.Section_SectionItem), +// which is the upper 2nd-level map specified by (activityId, chapterId). +// One key may correspond to multiple values, which are represented by a slice. +func (x *ActivityConf) FindAwardMap2(activityId uint64, chapterId uint32) ActivityConf_Index_AwardMap { + return x.indexAwardMap2[ActivityConf_LevelIndex_Activity_ChapterKey{activityId, chapterId}] +} + +// FindAward2 finds a slice of all values of the given key(s) in the upper 2nd-level map +// specified by (activityId, chapterId). +func (x *ActivityConf) FindAward2(activityId uint64, chapterId uint32, id uint32) []*protoconf.Section_SectionItem { + return x.FindAwardMap2(activityId, chapterId)[id] +} + +// FindFirstAward2 finds the first value of the given key(s) in the upper 2nd-level map +// specified by (activityId, chapterId), or nil if no value found. +func (x *ActivityConf) FindFirstAward2(activityId uint64, chapterId uint32, id uint32) *protoconf.Section_SectionItem { + val := x.FindAward2(activityId, chapterId, id) + if len(val) > 0 { + return val[0] + } + return nil +} + // ChapterConf is a wrapper around protobuf message: protoconf.ChapterConf. // // It is designed for three goals: @@ -406,7 +540,7 @@ func (x *ChapterConf) Data() *protoconf.ChapterConf { return nil } -// Load fills ChapterConf's inner message from file in the specified directory and format. +// Load loads ChapterConf's content in the given dir, based on format and messager options. func (x *ChapterConf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { start := time.Now() defer func() { @@ -423,7 +557,7 @@ func (x *ChapterConf) Load(dir string, format format.Format, opts *load.Messager return x.processAfterLoad() } -// Store writes ChapterConf's inner message to file in the specified directory and format. +// Store stores ChapterConf's content to file in the specified directory and format. // Available formats: JSON, Bin, and Text. func (x *ChapterConf) Store(dir string, format format.Format, options ...store.Option) error { return store.Store(x.Data(), dir, format, options...) @@ -447,7 +581,7 @@ func (x *ChapterConf) originalMessage() proto.Message { return nil } -// Get1 finds value in the 1-level map. It will return +// Get1 finds value in the 1st-level map. It will return // NotFound error if the key is not found. func (x *ChapterConf) Get1(id uint64) (*protoconf.ChapterConf_Chapter, error) { d := x.Data().GetChapterMap() @@ -483,7 +617,7 @@ func (x *ThemeConf) Data() *protoconf.ThemeConf { return nil } -// Load fills ThemeConf's inner message from file in the specified directory and format. +// Load loads ThemeConf's content in the given dir, based on format and messager options. func (x *ThemeConf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { start := time.Now() defer func() { @@ -500,7 +634,7 @@ func (x *ThemeConf) Load(dir string, format format.Format, opts *load.MessagerOp return x.processAfterLoad() } -// Store writes ThemeConf's inner message to file in the specified directory and format. +// Store stores ThemeConf's content to file in the specified directory and format. // Available formats: JSON, Bin, and Text. func (x *ThemeConf) Store(dir string, format format.Format, options ...store.Option) error { return store.Store(x.Data(), dir, format, options...) @@ -524,7 +658,7 @@ func (x *ThemeConf) originalMessage() proto.Message { return nil } -// Get1 finds value in the 1-level map. It will return +// Get1 finds value in the 1st-level map. It will return // NotFound error if the key is not found. func (x *ThemeConf) Get1(name string) (*protoconf.ThemeConf_Theme, error) { d := x.Data().GetThemeMap() @@ -535,7 +669,7 @@ func (x *ThemeConf) Get1(name string) (*protoconf.ThemeConf_Theme, error) { } } -// Get2 finds value in the 2-level map. It will return +// Get2 finds value in the 2nd-level map. It will return // NotFound error if the key is not found. func (x *ThemeConf) Get2(name string, param string) (string, error) { conf, err := x.Get1(name) @@ -609,7 +743,7 @@ func (x *TaskConf) Data() *protoconf.TaskConf { return nil } -// Load fills TaskConf's inner message from file in the specified directory and format. +// Load loads TaskConf's content in the given dir, based on format and messager options. func (x *TaskConf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { start := time.Now() defer func() { @@ -626,7 +760,7 @@ func (x *TaskConf) Load(dir string, format format.Format, opts *load.MessagerOpt return x.processAfterLoad() } -// Store writes TaskConf's inner message to file in the specified directory and format. +// Store stores TaskConf's content to file in the specified directory and format. // Available formats: JSON, Bin, and Text. func (x *TaskConf) Store(dir string, format format.Format, options ...store.Option) error { return store.Store(x.Data(), dir, format, options...) @@ -654,74 +788,85 @@ func (x *TaskConf) originalMessage() proto.Message { func (x *TaskConf) processAfterLoad() error { // Index init. x.indexTaskMap = make(TaskConf_Index_TaskMap) - for _, item1 := range x.data.GetTaskMap() { + for k1, v1 := range x.data.GetTaskMap() { + _ = k1 { // Index: ActivityID - key := item1.GetActivityId() - x.indexTaskMap[key] = append(x.indexTaskMap[key], item1) + key := v1.GetActivityId() + x.indexTaskMap[key] = append(x.indexTaskMap[key], v1) } } // Index(sort): ActivityID - for _, item := range x.indexTaskMap { - sort.Slice(item, func(i, j int) bool { - if item[i].GetGoal() != item[j].GetGoal() { - return item[i].GetGoal() < item[j].GetGoal() + indexTaskMapSorter := func(itemList []*protoconf.TaskConf_Task) func(i, j int) bool { + return func(i, j int) bool { + if itemList[i].GetGoal() != itemList[j].GetGoal() { + return itemList[i].GetGoal() < itemList[j].GetGoal() } - return item[i].GetId() < item[j].GetId() - }) + return itemList[i].GetId() < itemList[j].GetId() + } + } + for _, itemList := range x.indexTaskMap { + sort.Slice(itemList, indexTaskMapSorter(itemList)) } // OrderedIndex init. x.orderedIndexOrderedTaskMap = treemap.New[int64, []*protoconf.TaskConf_Task]() x.orderedIndexTaskExpiryMap = treemap.New[int64, []*protoconf.TaskConf_Task]() x.orderedIndexSortedTaskExpiryMap = treemap.New[int64, []*protoconf.TaskConf_Task]() x.orderedIndexActivityExpiryMap = treemap.New2[TaskConf_OrderedIndex_ActivityExpiryKey, []*protoconf.TaskConf_Task]() - for _, item1 := range x.data.GetTaskMap() { + for k1, v1 := range x.data.GetTaskMap() { + _ = k1 { // OrderedIndex: Goal@OrderedTask - key := item1.GetGoal() + key := v1.GetGoal() value, _ := x.orderedIndexOrderedTaskMap.Get(key) - x.orderedIndexOrderedTaskMap.Put(key, append(value, item1)) + x.orderedIndexOrderedTaskMap.Put(key, append(value, v1)) } { // OrderedIndex: Expiry@TaskExpiry - key := item1.GetExpiry().GetSeconds() + key := v1.GetExpiry().GetSeconds() value, _ := x.orderedIndexTaskExpiryMap.Get(key) - x.orderedIndexTaskExpiryMap.Put(key, append(value, item1)) + x.orderedIndexTaskExpiryMap.Put(key, append(value, v1)) } { // OrderedIndex: Expiry@SortedTaskExpiry - key := item1.GetExpiry().GetSeconds() + key := v1.GetExpiry().GetSeconds() value, _ := x.orderedIndexSortedTaskExpiryMap.Get(key) - x.orderedIndexSortedTaskExpiryMap.Put(key, append(value, item1)) + x.orderedIndexSortedTaskExpiryMap.Put(key, append(value, v1)) } { // OrderedIndex: (Expiry,ActivityID)@ActivityExpiry - key := TaskConf_OrderedIndex_ActivityExpiryKey{item1.GetExpiry().GetSeconds(), item1.GetActivityId()} + key := TaskConf_OrderedIndex_ActivityExpiryKey{v1.GetExpiry().GetSeconds(), v1.GetActivityId()} value, _ := x.orderedIndexActivityExpiryMap.Get(key) - x.orderedIndexActivityExpiryMap.Put(key, append(value, item1)) + x.orderedIndexActivityExpiryMap.Put(key, append(value, v1)) } } // OrderedIndex(sort): Goal@OrderedTask - x.orderedIndexOrderedTaskMap.Range(func(key int64, item []*protoconf.TaskConf_Task) bool { - sort.Slice(item, func(i, j int) bool { - return item[i].GetId() < item[j].GetId() - }) + orderedIndexOrderedTaskMapSorter := func(itemList []*protoconf.TaskConf_Task) func(i, j int) bool { + return func(i, j int) bool { + return itemList[i].GetId() < itemList[j].GetId() + } + } + x.orderedIndexOrderedTaskMap.Range(func(key int64, itemList []*protoconf.TaskConf_Task) bool { + sort.Slice(itemList, orderedIndexOrderedTaskMapSorter(itemList)) return true }) // OrderedIndex(sort): Expiry@SortedTaskExpiry - x.orderedIndexSortedTaskExpiryMap.Range(func(key int64, item []*protoconf.TaskConf_Task) bool { - sort.Slice(item, func(i, j int) bool { - if item[i].GetGoal() != item[j].GetGoal() { - return item[i].GetGoal() < item[j].GetGoal() + orderedIndexSortedTaskExpiryMapSorter := func(itemList []*protoconf.TaskConf_Task) func(i, j int) bool { + return func(i, j int) bool { + if itemList[i].GetGoal() != itemList[j].GetGoal() { + return itemList[i].GetGoal() < itemList[j].GetGoal() } - return item[i].GetId() < item[j].GetId() - }) + return itemList[i].GetId() < itemList[j].GetId() + } + } + x.orderedIndexSortedTaskExpiryMap.Range(func(key int64, itemList []*protoconf.TaskConf_Task) bool { + sort.Slice(itemList, orderedIndexSortedTaskExpiryMapSorter(itemList)) return true }) return nil } -// Get1 finds value in the 1-level map. It will return +// Get1 finds value in the 1st-level map. It will return // NotFound error if the key is not found. func (x *TaskConf) Get1(id int64) (*protoconf.TaskConf_Task, error) { d := x.Data().GetTaskMap() @@ -734,18 +879,18 @@ func (x *TaskConf) Get1(id int64) (*protoconf.TaskConf_Task, error) { // Index: ActivityID -// FindTaskMap finds the index (ActivityID) to value (protoconf.TaskConf_Task) map. -// One key may correspond to multiple values, which are contained by a slice. +// FindTaskMap finds the index: key(ActivityID) to value(protoconf.TaskConf_Task) map. +// One key may correspond to multiple values, which are represented by a slice. func (x *TaskConf) FindTaskMap() TaskConf_Index_TaskMap { return x.indexTaskMap } -// FindTask finds a slice of all values of the given key. +// FindTask finds a slice of all values of the given key(s). func (x *TaskConf) FindTask(activityId int64) []*protoconf.TaskConf_Task { return x.indexTaskMap[activityId] } -// FindFirstTask finds the first value of the given key, +// FindFirstTask finds the first value of the given key(s), // or nil if no value found. func (x *TaskConf) FindFirstTask(activityId int64) *protoconf.TaskConf_Task { val := x.FindTask(activityId) @@ -757,19 +902,19 @@ func (x *TaskConf) FindFirstTask(activityId int64) *protoconf.TaskConf_Task { // OrderedIndex: Goal@OrderedTask -// FindOrderedTaskMap finds the ordered index (Goal@OrderedTask) to value (protoconf.TaskConf_Task) treemap. -// One key may correspond to multiple values, which are contained by a slice. +// FindOrderedTaskMap finds the ordered index: key(Goal@OrderedTask) to value(protoconf.TaskConf_Task) treemap. +// One key may correspond to multiple values, which are represented by a slice. func (x *TaskConf) FindOrderedTaskMap() *TaskConf_OrderedIndex_OrderedTaskMap { return x.orderedIndexOrderedTaskMap } -// FindOrderedTask finds a slice of all values of the given key. +// FindOrderedTask finds a slice of all values of the given key(s). func (x *TaskConf) FindOrderedTask(goal int64) []*protoconf.TaskConf_Task { val, _ := x.orderedIndexOrderedTaskMap.Get(goal) return val } -// FindFirstOrderedTask finds the first value of the given key, +// FindFirstOrderedTask finds the first value of the given key(s), // or nil if no value found. func (x *TaskConf) FindFirstOrderedTask(goal int64) *protoconf.TaskConf_Task { val := x.FindOrderedTask(goal) @@ -781,19 +926,19 @@ func (x *TaskConf) FindFirstOrderedTask(goal int64) *protoconf.TaskConf_Task { // OrderedIndex: Expiry@TaskExpiry -// FindTaskExpiryMap finds the ordered index (Expiry@TaskExpiry) to value (protoconf.TaskConf_Task) treemap. -// One key may correspond to multiple values, which are contained by a slice. +// FindTaskExpiryMap finds the ordered index: key(Expiry@TaskExpiry) to value(protoconf.TaskConf_Task) treemap. +// One key may correspond to multiple values, which are represented by a slice. func (x *TaskConf) FindTaskExpiryMap() *TaskConf_OrderedIndex_TaskExpiryMap { return x.orderedIndexTaskExpiryMap } -// FindTaskExpiry finds a slice of all values of the given key. +// FindTaskExpiry finds a slice of all values of the given key(s). func (x *TaskConf) FindTaskExpiry(expiry int64) []*protoconf.TaskConf_Task { val, _ := x.orderedIndexTaskExpiryMap.Get(expiry) return val } -// FindFirstTaskExpiry finds the first value of the given key, +// FindFirstTaskExpiry finds the first value of the given key(s), // or nil if no value found. func (x *TaskConf) FindFirstTaskExpiry(expiry int64) *protoconf.TaskConf_Task { val := x.FindTaskExpiry(expiry) @@ -805,19 +950,19 @@ func (x *TaskConf) FindFirstTaskExpiry(expiry int64) *protoconf.TaskConf_Task { // OrderedIndex: Expiry@SortedTaskExpiry -// FindSortedTaskExpiryMap finds the ordered index (Expiry@SortedTaskExpiry) to value (protoconf.TaskConf_Task) treemap. -// One key may correspond to multiple values, which are contained by a slice. +// FindSortedTaskExpiryMap finds the ordered index: key(Expiry@SortedTaskExpiry) to value(protoconf.TaskConf_Task) treemap. +// One key may correspond to multiple values, which are represented by a slice. func (x *TaskConf) FindSortedTaskExpiryMap() *TaskConf_OrderedIndex_SortedTaskExpiryMap { return x.orderedIndexSortedTaskExpiryMap } -// FindSortedTaskExpiry finds a slice of all values of the given key. +// FindSortedTaskExpiry finds a slice of all values of the given key(s). func (x *TaskConf) FindSortedTaskExpiry(expiry int64) []*protoconf.TaskConf_Task { val, _ := x.orderedIndexSortedTaskExpiryMap.Get(expiry) return val } -// FindFirstSortedTaskExpiry finds the first value of the given key, +// FindFirstSortedTaskExpiry finds the first value of the given key(s), // or nil if no value found. func (x *TaskConf) FindFirstSortedTaskExpiry(expiry int64) *protoconf.TaskConf_Task { val := x.FindSortedTaskExpiry(expiry) @@ -829,19 +974,19 @@ func (x *TaskConf) FindFirstSortedTaskExpiry(expiry int64) *protoconf.TaskConf_T // OrderedIndex: (Expiry,ActivityID)@ActivityExpiry -// FindActivityExpiryMap finds the ordered index ((Expiry,ActivityID)@ActivityExpiry) to value (protoconf.TaskConf_Task) treemap. -// One key may correspond to multiple values, which are contained by a slice. +// FindActivityExpiryMap finds the ordered index: key((Expiry,ActivityID)@ActivityExpiry) to value(protoconf.TaskConf_Task) treemap. +// One key may correspond to multiple values, which are represented by a slice. func (x *TaskConf) FindActivityExpiryMap() *TaskConf_OrderedIndex_ActivityExpiryMap { return x.orderedIndexActivityExpiryMap } -// FindActivityExpiry finds a slice of all values of the given key. +// FindActivityExpiry finds a slice of all values of the given key(s). func (x *TaskConf) FindActivityExpiry(expiry int64, activityId int64) []*protoconf.TaskConf_Task { val, _ := x.orderedIndexActivityExpiryMap.Get(TaskConf_OrderedIndex_ActivityExpiryKey{expiry, activityId}) return val } -// FindFirstActivityExpiry finds the first value of the given key, +// FindFirstActivityExpiry finds the first value of the given key(s), // or nil if no value found. func (x *TaskConf) FindFirstActivityExpiry(expiry int64, activityId int64) *protoconf.TaskConf_Task { val := x.FindActivityExpiry(expiry, activityId) diff --git a/test/proto/index_conf.proto b/test/proto/index_conf.proto new file mode 100644 index 00000000..f990160f --- /dev/null +++ b/test/proto/index_conf.proto @@ -0,0 +1,87 @@ +syntax = "proto3"; + +package protoconf; + +import "tableau/protobuf/tableau.proto"; +import "common_conf.proto"; + +option go_package = "github.com/tableauio/loader/test/go-tableau-loader/protoconf"; +option (tableau.workbook) = { + name: "Index.xlsx" +}; + +// Nesting: map -> map +message FruitConf { + option (tableau.worksheet) = { + name: "FruitConf" + ordered_index: "Price" + }; + + map fruit_map = 1 [(tableau.field) = { key: "FruitType" layout: LAYOUT_VERTICAL }]; + message Fruit { + protoconf.FruitType fruit_type = 1 [(tableau.field) = { name: "FruitType" }]; + map item_map = 2 [(tableau.field) = { key: "ID" layout: LAYOUT_VERTICAL }]; + message Item { + int32 id = 1 [(tableau.field) = { name: "ID" }]; + int32 price = 2 [(tableau.field) = { name: "Price" }]; + } + } +} + +// Nesting: map -> list -> map -> list +message Fruit2Conf { + option (tableau.worksheet) = { + name: "Fruit2Conf" + index: "CountryName" + index: "CountryItemAttrName" + ordered_index: "CountryItemPrice" + }; + + map fruit_map = 1 [(tableau.field) = { key: "FruitType" layout: LAYOUT_VERTICAL }]; + message Fruit { + protoconf.FruitType fruit_type = 1 [(tableau.field) = { name: "FruitType" }]; + repeated Country country_list = 2 [(tableau.field) = { name: "Country" layout: LAYOUT_HORIZONTAL }]; + message Country { + string name = 1 [(tableau.field) = { name: "Name" }]; + map item_map = 2 [(tableau.field) = { name: "Item" key: "ID" layout: LAYOUT_HORIZONTAL }]; + message Item { + int32 id = 1 [(tableau.field) = { name: "ID" }]; + int32 price = 2 [(tableau.field) = { name: "Price" }]; + repeated Attr attr_list = 3 [(tableau.field) = { name: "Attr" layout: LAYOUT_HORIZONTAL }]; + message Attr { + string name = 1 [(tableau.field) = { name: "Name" }]; + int32 value = 2 [(tableau.field) = { name: "Value" }]; + } + } + } + } +} + +// Nesting: list -> list -> map -> list +message Fruit3Conf { + option (tableau.worksheet) = { + name: "Fruit2Conf" + index: "CountryName" + index: "CountryItemAttrName" + ordered_index: "CountryItemPrice" + }; + + repeated Fruit fruit_list = 1 [(tableau.field) = { layout: LAYOUT_VERTICAL }]; + message Fruit { + protoconf.FruitType fruit_type = 1 [(tableau.field) = { name: "FruitType" }]; + repeated Country country_list = 2 [(tableau.field) = { name: "Country" layout: LAYOUT_HORIZONTAL }]; + message Country { + string name = 1 [(tableau.field) = { name: "Name" }]; + map item_map = 2 [(tableau.field) = { name: "Item" key: "ID" layout: LAYOUT_HORIZONTAL }]; + message Item { + int32 id = 1 [(tableau.field) = { name: "ID" }]; + int32 price = 2 [(tableau.field) = { name: "Price" }]; + repeated Attr attr_list = 3 [(tableau.field) = { name: "Attr" layout: LAYOUT_HORIZONTAL }]; + message Attr { + string name = 1 [(tableau.field) = { name: "Name" }]; + int32 value = 2 [(tableau.field) = { name: "Value" }]; + } + } + } + } +} \ No newline at end of file diff --git a/test/proto/item_conf.proto b/test/proto/item_conf.proto index 7c143864..ed873b91 100644 --- a/test/proto/item_conf.proto +++ b/test/proto/item_conf.proto @@ -9,7 +9,7 @@ import "common_conf.proto"; option go_package = "github.com/tableauio/loader/test/go-tableau-loader/protoconf"; option (tableau.workbook) = { - name: "Test.xlsx" + name: "Item.xlsx" }; message ItemConf { @@ -77,4 +77,4 @@ message UseEffect { uint32 item_id = 1 [(tableau.field) = { name: "ItemId" }]; } message AccountLevel {} -} \ No newline at end of file +} diff --git a/test/testdata/conf/Fruit2Conf.json b/test/testdata/conf/Fruit2Conf.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/test/testdata/conf/Fruit2Conf.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/testdata/conf/Fruit3Conf.json b/test/testdata/conf/Fruit3Conf.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/test/testdata/conf/Fruit3Conf.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/testdata/conf/FruitConf.json b/test/testdata/conf/FruitConf.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/test/testdata/conf/FruitConf.json @@ -0,0 +1 @@ +{} \ No newline at end of file