diff --git a/cmd/protoc-gen-cpp-tableau-loader/index.go b/cmd/protoc-gen-cpp-tableau-loader/index.go index 112826bb..ce9f408d 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/index.go @@ -10,142 +10,149 @@ import ( "google.golang.org/protobuf/compiler/protogen" ) -func genHppIndexFinders(g *protogen.GeneratedFile, descriptors []*index.IndexDescriptor) { +func genHppIndexFinders(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { g.P(" // Index accessers.") - for _, descriptor := range descriptors { - if len(descriptor.Fields) == 1 { - // single-column index - field := descriptor.Fields[0] // just take first field - g.P(" // Index: ", descriptor.Index) - g.P(" public:") - vectorType := fmt.Sprintf("Index_%sVector", descriptor.Name) - mapType := fmt.Sprintf("Index_%sMap", descriptor.Name) - g.P(" using ", vectorType, " = std::vector;") - keyType := helper.ParseCppType(field.FD) - g.P(" using ", mapType, " = std::unordered_map<", keyType, ", ", vectorType, ">;") - g.P(" const ", mapType, "& Find", descriptor.Name, "() const;") - g.P(" const ", vectorType, "* Find", descriptor.Name, "(", helper.ToConstRefType(keyType), " ", helper.ParseIndexFieldNameAsFuncParam(field.FD), ") const;") - g.P(" const ", helper.ParseCppClassType(descriptor.MD), "* FindFirst", descriptor.Name, "(", helper.ToConstRefType(keyType), " ", helper.ParseIndexFieldNameAsFuncParam(field.FD), ") const;") - g.P() + for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + g.P(" // Index: ", index.Index) + g.P(" public:") + vectorType := fmt.Sprintf("Index_%sVector", index.Name()) + mapType := fmt.Sprintf("Index_%sMap", index.Name()) + g.P(" using ", vectorType, " = std::vector;") + keyType := helper.ParseCppType(field.FD) + g.P(" using ", mapType, " = std::unordered_map<", keyType, ", ", vectorType, ">;") + g.P(" const ", mapType, "& Find", index.Name(), "() const;") + g.P(" const ", vectorType, "* Find", index.Name(), "(", helper.ToConstRefType(keyType), " ", helper.ParseIndexFieldNameAsFuncParam(field.FD), ") const;") + g.P(" const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(", helper.ToConstRefType(keyType), " ", helper.ParseIndexFieldNameAsFuncParam(field.FD), ") const;") + g.P() - g.P(" private:") - indexContainerName := "index_" + strcase.ToSnake(descriptor.Name) + "_map_" - g.P(" ", mapType, " ", indexContainerName, ";") - g.P() - } else { - // multi-column index - g.P(" // Index: ", descriptor.Index) - g.P(" public:") - keyType := fmt.Sprintf("Index_%sKey", descriptor.Name) - keyHasherType := fmt.Sprintf("Index_%sKeyHasher", descriptor.Name) - vectorType := fmt.Sprintf("Index_%sVector", descriptor.Name) - mapType := fmt.Sprintf("Index_%sMap", descriptor.Name) - - // generate key struct - g.P(" struct ", keyType, " {") - equality := "" - for i, field := range descriptor.Fields { - g.P(" ", helper.ParseCppType(field.FD), " ", helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD), ";") - equality += helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD) + " == other." + helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD) - if i != len(descriptor.Fields)-1 { - equality += " && " + g.P(" private:") + indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" + g.P(" ", mapType, " ", indexContainerName, ";") + g.P() + } else { + // multi-column index + g.P(" // Index: ", index.Index) + g.P(" public:") + keyType := fmt.Sprintf("Index_%sKey", index.Name()) + keyHasherType := fmt.Sprintf("Index_%sKeyHasher", index.Name()) + vectorType := fmt.Sprintf("Index_%sVector", index.Name()) + mapType := fmt.Sprintf("Index_%sMap", index.Name()) + + // generate key struct + g.P(" struct ", keyType, " {") + equality := "" + for i, field := range index.ColFields { + g.P(" ", helper.ParseCppType(field.FD), " ", helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD), ";") + equality += helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD) + " == other." + helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD) + if i != len(index.ColFields)-1 { + equality += " && " + } } - } - g.P(" bool operator==(const ", keyType, "& other) const {") - g.P(" return ", equality, ";") - g.P(" }") - g.P(" };") - - // generate key hasher struct - g.P(" struct ", keyHasherType, " {") - combinedKeys := "" - for i, field := range descriptor.Fields { - key := "key." + helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD) - combinedKeys += key - if i != len(descriptor.Fields)-1 { - combinedKeys += ", " + g.P(" bool operator==(const ", keyType, "& other) const {") + g.P(" return ", equality, ";") + g.P(" }") + g.P(" };") + + // generate key hasher struct + g.P(" struct ", keyHasherType, " {") + combinedKeys := "" + for i, field := range index.ColFields { + key := "key." + helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD) + combinedKeys += key + if i != len(index.ColFields)-1 { + combinedKeys += ", " + } } - } - g.P(" std::size_t operator()(const ", keyType, "& key) const {") - g.P(" return util::SugaredHashCombine(", combinedKeys, ");") - g.P(" }") - g.P(" };") - - g.P(" using ", vectorType, " = std::vector;") - g.P(" using ", mapType, " = std::unordered_map<", keyType, ", ", vectorType, ", ", keyHasherType, ">;") - g.P(" const ", mapType, "& Find", descriptor.Name, "() const;") - g.P(" const ", vectorType, "* Find", descriptor.Name, "(const ", keyType, "& key) const;") - g.P(" const ", helper.ParseCppClassType(descriptor.MD), "* FindFirst", descriptor.Name, "(const ", keyType, "& key) const;") - g.P() + g.P(" std::size_t operator()(const ", keyType, "& key) const {") + g.P(" return util::SugaredHashCombine(", combinedKeys, ");") + g.P(" }") + g.P(" };") - g.P(" private:") - indexContainerName := "index_" + strcase.ToSnake(descriptor.Name) + "_map_" - g.P(" ", mapType, " ", indexContainerName, ";") - g.P() + g.P(" using ", vectorType, " = std::vector;") + g.P(" using ", mapType, " = std::unordered_map<", keyType, ", ", vectorType, ", ", keyHasherType, ">;") + g.P(" const ", mapType, "& Find", index.Name(), "() const;") + g.P(" const ", vectorType, "* Find", index.Name(), "(const ", keyType, "& key) const;") + g.P(" const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(const ", keyType, "& key) const;") + g.P() + + g.P(" private:") + indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" + g.P(" ", mapType, " ", indexContainerName, ";") + g.P() + } } } } -func genCppIndexLoader(g *protogen.GeneratedFile, descriptors []*index.IndexDescriptor) { +func genCppIndexLoader(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { g.P(" // Index init.") - for _, descriptor := range descriptors { - parentDataName := "data_" - g.P(" // Index: ", descriptor.Index) - genOneCppIndexLoader(g, 1, descriptor, parentDataName, descriptor.LevelMessage) - } -} - -func genOneCppIndexLoader(g *protogen.GeneratedFile, depth int, descriptor *index.IndexDescriptor, parentDataName string, levelMessage *index.LevelMessage) { - if levelMessage == nil { - return + for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" + g.P(" ", indexContainerName, ".clear();") + } } - indexContainerName := "index_" + strcase.ToSnake(descriptor.Name) + "_map_" - if levelMessage.NextLevel != nil { + parentDataName := "data_" + depth := 1 + for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + genOneCppIndexLoader(g, depth, index, parentDataName) + } itemName := fmt.Sprintf("item%d", depth) + if levelMessage.FD == nil { + break + } g.P(strings.Repeat(" ", depth), "for (auto&& "+itemName+" : "+parentDataName+"."+helper.ParseIndexFieldName(levelMessage.FD)+"()) {") - parentDataName = itemName if levelMessage.FD.IsMap() { parentDataName = itemName + ".second" } - genOneCppIndexLoader(g, depth+1, descriptor, parentDataName, levelMessage.NextLevel) - g.P(strings.Repeat(" ", depth), "}") - } else { - if len(levelMessage.Fields) == 1 { - // single-column index - field := levelMessage.Fields[0] // just take the first field - if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += "." + helper.ParseIndexFieldName(leveledFd) + "()" - } - g.P(strings.Repeat(" ", depth), "for (auto&& "+itemName+" : "+parentDataName+fieldName+") {") - key := itemName - if field.FD.Enum() != nil { - key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" - } - g.P(strings.Repeat(" ", depth+1), indexContainerName, "["+key+"].push_back(&"+parentDataName+");") - g.P(strings.Repeat(" ", depth), "}") - } else { - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += "." + helper.ParseIndexFieldName(leveledFd) + "()" - } - key := parentDataName + fieldName - g.P(strings.Repeat(" ", depth), indexContainerName, "["+key+"].push_back(&"+parentDataName+");") + defer g.P(strings.Repeat(" ", depth), "}") + depth++ + } +} + +func genOneCppIndexLoader(g *protogen.GeneratedFile, depth int, index *index.LevelIndex, parentDataName string) { + indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" + g.P(strings.Repeat(" ", depth), "{") + g.P(strings.Repeat(" ", depth+1), "// Index: ", index.Index) + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take the first field + if field.FD.IsList() { + itemName := fmt.Sprintf("item%d", depth) + fieldName := "" + for _, leveledFd := range field.LeveledFDList { + fieldName += "." + helper.ParseIndexFieldName(leveledFd) + "()" } + g.P(strings.Repeat(" ", depth+1), "for (auto&& "+itemName+" : "+parentDataName+fieldName+") {") + key := itemName + if field.FD.Enum() != nil { + key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" + } + g.P(strings.Repeat(" ", depth+2), indexContainerName, "["+key+"].push_back(&"+parentDataName+");") + g.P(strings.Repeat(" ", depth+1), "}") } else { - // multi-column index - var keys []string - generateOneCppMulticolumnIndex(g, depth, descriptor, parentDataName, keys) + fieldName := "" + for _, leveledFd := range field.LeveledFDList { + fieldName += "." + helper.ParseIndexFieldName(leveledFd) + "()" + } + key := parentDataName + fieldName + g.P(strings.Repeat(" ", depth+1), indexContainerName, "["+key+"].push_back(&"+parentDataName+");") } + } else { + // multi-column index + generateOneCppMulticolumnIndex(g, depth, index, parentDataName, nil) } - if depth == 1 && len(descriptor.KeyFields) != 0 { - g.P(strings.Repeat(" ", depth), "for (auto&& item : ", indexContainerName, ") {") - g.P(strings.Repeat(" ", depth+1), "std::sort(item.second.begin(), item.second.end(),") - g.P(strings.Repeat(" ", depth+6), "[](const ", helper.ParseCppClassType(descriptor.MD), "* a, const ", helper.ParseCppClassType(descriptor.MD), "* b) {") - for i, field := range descriptor.KeyFields { + if len(index.KeyFields) != 0 { + g.P(strings.Repeat(" ", depth+1), "for (auto&& item : ", indexContainerName, ") {") + g.P(strings.Repeat(" ", depth+2), "std::sort(item.second.begin(), item.second.end(),") + g.P(strings.Repeat(" ", depth+7), "[](const ", helper.ParseCppClassType(index.MD), "* a, const ", helper.ParseCppClassType(index.MD), "* b) {") + for i, field := range index.KeyFields { fieldName := "" for i, leveledFd := range field.LeveledFDList { accessOperator := "." @@ -154,22 +161,23 @@ func genOneCppIndexLoader(g *protogen.GeneratedFile, depth int, descriptor *inde } fieldName += accessOperator + helper.ParseIndexFieldName(leveledFd) + "()" } - if i == len(descriptor.KeyFields)-1 { - g.P(strings.Repeat(" ", depth+7), "return a", fieldName, " < b", fieldName, ";") - } else { - g.P(strings.Repeat(" ", depth+7), "if (a", fieldName, " != b", fieldName, ") {") + if i == len(index.KeyFields)-1 { g.P(strings.Repeat(" ", depth+8), "return a", fieldName, " < b", fieldName, ";") - g.P(strings.Repeat(" ", depth+7), "}") + } else { + g.P(strings.Repeat(" ", depth+8), "if (a", fieldName, " != b", fieldName, ") {") + g.P(strings.Repeat(" ", depth+9), "return a", fieldName, " < b", fieldName, ";") + g.P(strings.Repeat(" ", depth+8), "}") } } - g.P(strings.Repeat(" ", depth+6), "});") - g.P(strings.Repeat(" ", depth), "}") + g.P(strings.Repeat(" ", depth+7), "});") + g.P(strings.Repeat(" ", depth+1), "}") } + g.P(strings.Repeat(" ", depth), "}") } -func generateOneCppMulticolumnIndex(g *protogen.GeneratedFile, depth int, descriptor *index.IndexDescriptor, parentDataName string, keys []string) []string { +func generateOneCppMulticolumnIndex(g *protogen.GeneratedFile, depth int, index *index.LevelIndex, parentDataName string, keys []string) []string { cursor := len(keys) - if cursor >= len(descriptor.Fields) { + if cursor >= len(index.ColFields) { var keyParams string for i, key := range keys { keyParams += key @@ -177,27 +185,27 @@ func generateOneCppMulticolumnIndex(g *protogen.GeneratedFile, depth int, descri keyParams += ", " } } - keyType := fmt.Sprintf("Index_%sKey", descriptor.Name) - indexContainerName := "index_" + strcase.ToSnake(descriptor.Name) + "_map_" - g.P(strings.Repeat(" ", depth), keyType, " key{", keyParams, "};") - g.P(strings.Repeat(" ", depth), indexContainerName, "[key].push_back(&"+parentDataName+");") + keyType := fmt.Sprintf("Index_%sKey", index.Name()) + indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" + g.P(strings.Repeat(" ", depth+1), keyType, " key{", keyParams, "};") + g.P(strings.Repeat(" ", depth+1), indexContainerName, "[key].push_back(&"+parentDataName+");") return keys } - field := descriptor.Fields[cursor] + field := index.ColFields[cursor] if field.FD.IsList() { itemName := fmt.Sprintf("index_item%d", cursor) fieldName := "" for _, leveledFd := range field.LeveledFDList { fieldName += "." + helper.ParseIndexFieldName(leveledFd) + "()" } - g.P(strings.Repeat(" ", depth), "for (auto&& "+itemName+" : "+parentDataName+fieldName+") {") + g.P(strings.Repeat(" ", depth+1), "for (auto&& "+itemName+" : "+parentDataName+fieldName+") {") key := itemName if field.FD.Enum() != nil { key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" } keys = append(keys, key) - keys = generateOneCppMulticolumnIndex(g, depth+1, descriptor, parentDataName, keys) - g.P(strings.Repeat(" ", depth), "}") + keys = generateOneCppMulticolumnIndex(g, depth+1, index, parentDataName, keys) + g.P(strings.Repeat(" ", depth+1), "}") } else { fieldName := "" for _, leveledFd := range field.LeveledFDList { @@ -205,50 +213,51 @@ func generateOneCppMulticolumnIndex(g *protogen.GeneratedFile, depth int, descri } key := parentDataName + fieldName keys = append(keys, key) - keys = generateOneCppMulticolumnIndex(g, depth, descriptor, parentDataName, keys) + keys = generateOneCppMulticolumnIndex(g, depth, index, parentDataName, keys) } return keys } -func genCppIndexFinders(g *protogen.GeneratedFile, descriptors []*index.IndexDescriptor, messagerName string) { - for _, descriptor := range descriptors { - vectorType := "Index_" + descriptor.Name + "Vector" - mapType := "Index_" + descriptor.Name + "Map" - indexContainerName := "index_" + strcase.ToSnake(descriptor.Name) + "_map_" - - g.P("// Index: ", descriptor.Index) - g.P("const ", messagerName, "::", mapType, "& "+messagerName+"::Find", descriptor.Name, "() const { return "+indexContainerName+" ;}") - g.P() - - var keyType, keyName string - if len(descriptor.Fields) == 1 { - // single-column index - field := descriptor.Fields[0] // just take first field - keyType = helper.ParseCppType(field.FD) - keyName = helper.ParseIndexFieldNameAsFuncParam(field.FD) - } else { - // multi-column index - keyType = fmt.Sprintf("const Index_%sKey&", descriptor.Name) - keyName = "key" - } +func genCppIndexFinders(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { + for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + vectorType := "Index_" + index.Name() + "Vector" + mapType := "Index_" + index.Name() + "Map" + indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" - g.P("const ", messagerName, "::", vectorType, "* "+messagerName+"::Find", descriptor.Name, "(", helper.ToConstRefType(keyType), " ", keyName, ") const {") - g.P(" auto iter = ", indexContainerName, ".find(", keyName, ");") - g.P(" if (iter == ", indexContainerName, ".end()) {") - g.P(" return nullptr;") - g.P(" }") - g.P(" return &iter->second;") - g.P("}") - g.P() - - g.P("const ", helper.ParseCppClassType(descriptor.MD), "* "+messagerName+"::FindFirst", descriptor.Name, "(", helper.ToConstRefType(keyType), " ", keyName, ") const {") - g.P(" auto conf = Find", descriptor.Name, "(", keyName, ");") - g.P(" if (conf == nullptr || conf->size() == 0) {") - g.P(" return nullptr;") - g.P(" }") - g.P(" return (*conf)[0];") - g.P("}") - g.P() + g.P("// Index: ", index.Index) + g.P("const ", messagerName, "::", mapType, "& "+messagerName+"::Find", index.Name(), "() const { return "+indexContainerName+" ;}") + g.P() + var keyType, keyName string + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + keyType = helper.ParseCppType(field.FD) + keyName = helper.ParseIndexFieldNameAsFuncParam(field.FD) + } else { + // multi-column index + keyType = fmt.Sprintf("const Index_%sKey&", index.Name()) + keyName = "key" + } + + g.P("const ", messagerName, "::", vectorType, "* "+messagerName+"::Find", index.Name(), "(", helper.ToConstRefType(keyType), " ", keyName, ") const {") + g.P(" auto iter = ", indexContainerName, ".find(", keyName, ");") + g.P(" if (iter == ", indexContainerName, ".end()) {") + g.P(" return nullptr;") + g.P(" }") + g.P(" return &iter->second;") + g.P("}") + g.P() + + g.P("const ", helper.ParseCppClassType(index.MD), "* "+messagerName+"::FindFirst", index.Name(), "(", helper.ToConstRefType(keyType), " ", keyName, ") const {") + g.P(" auto conf = Find", index.Name(), "(", keyName, ");") + g.P(" if (conf == nullptr || conf->size() == 0) {") + g.P(" return nullptr;") + g.P(" }") + g.P(" return (*conf)[0];") + g.P("}") + g.P() + } } } diff --git a/cmd/protoc-gen-cpp-tableau-loader/messager.go b/cmd/protoc-gen-cpp-tableau-loader/messager.go index 2fdd8b13..4a709add 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/messager.go +++ b/cmd/protoc-gen-cpp-tableau-loader/messager.go @@ -80,7 +80,7 @@ func genHppMessage(gen *protogen.Plugin, file *protogen.File, g *protogen.Genera pkg := string(file.Desc.Package()) cppFullName := strings.ReplaceAll(pkg, ".", "::") + "::" + string(message.Desc.Name()) messagerFullName := string(message.Desc.FullName()) - indexDescriptors := index.ParseIndexDescriptor(gen, message.Desc) + indexDescriptor := index.ParseIndexDescriptor(message.Desc) g.P("class ", message.Desc.Name(), " : public Messager {") g.P(" public:") @@ -108,7 +108,7 @@ func genHppMessage(gen *protogen.Plugin, file *protogen.File, g *protogen.Genera } if options.NeedGenIndex(message.Desc, options.LangCPP) { g.P() - genHppIndexFinders(g, indexDescriptors) + genHppIndexFinders(g, indexDescriptor) } g.P("};") g.P() @@ -154,7 +154,7 @@ func generateCppFileContent(gen *protogen.Plugin, file *protogen.File, g *protog func genCppMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protogen.Message) { messagerName := string(message.Desc.Name()) messagerFullName := string(message.Desc.FullName()) - indexDescriptors := index.ParseIndexDescriptor(gen, message.Desc) + indexDescriptor := index.ParseIndexDescriptor(message.Desc) g.P("const std::string ", messagerName, "::kProtoName = ", `"`, messagerName, `";`) g.P() @@ -173,7 +173,7 @@ func genCppMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *pro genCppOrderedMapLoader(g, message.Desc, 1, messagerFullName) } if options.NeedGenIndex(message.Desc, options.LangCPP) { - genCppIndexLoader(g, indexDescriptors) + genCppIndexLoader(g, indexDescriptor) } g.P(" return true;") g.P("}") @@ -184,7 +184,7 @@ func genCppMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *pro genCppMapGetters(g, message.Desc, 1, nil, messagerName) genCppOrderedMapGetters(g, message.Desc, 1, nil, messagerName, messagerFullName) if options.NeedGenIndex(message.Desc, options.LangCPP) { - genCppIndexFinders(g, indexDescriptors, messagerName) + genCppIndexFinders(g, indexDescriptor, messagerName) g.P() } } diff --git a/cmd/protoc-gen-cpp-tableau-loader/ordered_map.go b/cmd/protoc-gen-cpp-tableau-loader/ordered_map.go index a32757f2..496a2a6b 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/ordered_map.go +++ b/cmd/protoc-gen-cpp-tableau-loader/ordered_map.go @@ -62,6 +62,7 @@ func genHppOrderedMapGetters(g *protogen.GeneratedFile, md protoreflect.MessageD func genCppOrderedMapLoader(g *protogen.GeneratedFile, md protoreflect.MessageDescriptor, depth int, messagerFullName string) { if depth == 1 { g.P(" // OrderedMap init.") + g.P(" ordered_map_.clear();") } for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) diff --git a/cmd/protoc-gen-go-tableau-loader/index.go b/cmd/protoc-gen-go-tableau-loader/index.go index b5e32833..e61af74b 100644 --- a/cmd/protoc-gen-go-tableau-loader/index.go +++ b/cmd/protoc-gen-go-tableau-loader/index.go @@ -9,104 +9,111 @@ import ( "google.golang.org/protobuf/compiler/protogen" ) -func genIndexTypeDef(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptors []*index.IndexDescriptor, messagerName string) { +func genIndexTypeDef(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { g.P("// Index types.") - for _, descriptor := range descriptors { - if len(descriptor.Fields) == 1 { - // single-column index - field := descriptor.Fields[0] // just take first field - g.P("// Index: ", descriptor.Index) - mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, descriptor.Name) - keyType := helper.ParseGoType(gen, field.FD) - g.P("type ", mapType, " = map[", keyType, "][]*", helper.FindMessageGoIdent(gen, descriptor.MD)) - } else { - // multi-column index - g.P("// Index: ", descriptor.Index) - keyType := fmt.Sprintf("%s_Index_%sKey", messagerName, descriptor.Name) - mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, descriptor.Name) + for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + g.P("// Index: ", index.Index) + mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, index.Name()) + keyType := helper.ParseGoType(gen, field.FD) + g.P("type ", mapType, " = map[", keyType, "][]*", helper.FindMessageGoIdent(gen, index.MD)) + } else { + // multi-column index + g.P("// Index: ", index.Index) + keyType := fmt.Sprintf("%s_Index_%sKey", messagerName, index.Name()) + mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, index.Name()) - // generate key struct - // KeyType must be comparable, refer https://go.dev/blog/maps - g.P("type ", keyType, " struct {") - for _, field := range descriptor.Fields { - g.P(helper.ParseIndexFieldNameAsKeyStructFieldName(gen, field.FD), " ", helper.ParseGoType(gen, field.FD)) + // generate key struct + // KeyType must be comparable, refer https://go.dev/blog/maps + g.P("type ", keyType, " struct {") + for _, field := range index.ColFields { + g.P(helper.ParseIndexFieldNameAsKeyStructFieldName(gen, field.FD), " ", helper.ParseGoType(gen, field.FD)) + } + g.P("}") + g.P("type ", mapType, " = map[", keyType, "][]*", helper.FindMessageGoIdent(gen, index.MD)) } - g.P("}") - g.P("type ", mapType, " = map[", keyType, "][]*", helper.FindMessageGoIdent(gen, descriptor.MD)) + g.P() } - g.P() } } -func genIndexField(g *protogen.GeneratedFile, descriptors []*index.IndexDescriptor, messagerName string) { - for _, descriptor := range descriptors { - indexContainerName := "index" + strcase.ToCamel(descriptor.Name) + "Map" - mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, descriptor.Name) - g.P(indexContainerName, " ", mapType) +func genIndexField(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { + for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + indexContainerName := "index" + strcase.ToCamel(index.Name()) + "Map" + mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, index.Name()) + g.P(indexContainerName, " ", mapType) + } } } -func genIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptors []*index.IndexDescriptor, messagerName string) { +func genIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { g.P(" // Index init.") - for _, descriptor := range descriptors { - parentDataName := "x.data" - g.P(" // Index: ", descriptor.Index) - genOneIndexLoader(gen, g, 1, descriptor, parentDataName, descriptor.LevelMessage, messagerName) - } -} - -func genOneIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, depth int, descriptor *index.IndexDescriptor, - parentDataName string, levelMessage *index.LevelMessage, messagerName string) { - if levelMessage == nil { - return - } - indexContainerName := "index" + strcase.ToCamel(descriptor.Name) + "Map" - if depth == 1 { - g.P("x.", indexContainerName, " = make(", fmt.Sprintf("%s_Index_%sMap", messagerName, descriptor.Name), ")") + for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + indexContainerName := "index" + strcase.ToCamel(index.Name()) + "Map" + g.P("x.", indexContainerName, " = make(", fmt.Sprintf("%s_Index_%sMap", messagerName, index.Name()), ")") + } } - if levelMessage.NextLevel != nil { + parentDataName := "x.data" + depth := 1 + for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + genOneIndexLoader(gen, g, depth, index, parentDataName, messagerName) + } itemName := fmt.Sprintf("item%d", depth) + if levelMessage.FD == nil { + break + } g.P("for _, ", itemName, " := range "+parentDataName+".Get"+helper.ParseIndexFieldName(gen, levelMessage.FD)+"() {") parentDataName = itemName - genOneIndexLoader(gen, g, depth+1, descriptor, parentDataName, levelMessage.NextLevel, messagerName) - g.P("}") - } else { - if len(levelMessage.Fields) == 1 { - // single-column index - field := levelMessage.Fields[0] // just take the first field - if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" - } - g.P("for _, ", itemName, " := range "+parentDataName+fieldName+" {") - g.P("key := ", itemName) - g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") - g.P("}") - } else { - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" - } - g.P("key := ", parentDataName+fieldName) - g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") + defer g.P("}") + depth++ + } +} + +func genOneIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, depth int, index *index.LevelIndex, + parentDataName string, messagerName string) { + indexContainerName := "index" + strcase.ToCamel(index.Name()) + "Map" + g.P("{") + g.P(" // Index: ", index.Index) + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take the first field + if field.FD.IsList() { + itemName := fmt.Sprintf("item%d", depth) + fieldName := "" + for _, leveledFd := range field.LeveledFDList { + fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" } + g.P("for _, ", itemName, " := range "+parentDataName+fieldName+" {") + g.P("key := ", itemName) + g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") + g.P("}") } else { - // multi-column index - var keys []string - generateOneMulticolumnIndex(gen, g, depth, descriptor, parentDataName, keys) + fieldName := "" + for _, leveledFd := range field.LeveledFDList { + fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" + } + g.P("key := ", parentDataName+fieldName) + g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") } + } else { + // multi-column index + generateOneMulticolumnIndex(gen, g, depth, index, parentDataName, messagerName, nil) } - if depth == 1 && len(descriptor.KeyFields) != 0 { + if len(index.KeyFields) != 0 { g.P("for _, item := range x.", indexContainerName, " {") g.P(sortPackage.Ident("Slice"), "(item, func(i, j int) bool {") - for i, field := range descriptor.KeyFields { + for i, field := range index.KeyFields { fieldName := "" for _, leveledFd := range field.LeveledFDList { fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" } - if i == len(descriptor.KeyFields)-1 { + if i == len(index.KeyFields)-1 { g.P("return item[i]", fieldName, " < item[j]", fieldName) } else { g.P("if item[i]", fieldName, " != item[j]", fieldName, " {") @@ -117,12 +124,13 @@ func genOneIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, depth in g.P("})") g.P("}") } + g.P("}") } func generateOneMulticolumnIndex(gen *protogen.Plugin, g *protogen.GeneratedFile, - depth int, descriptor *index.IndexDescriptor, parentDataName string, keys []string) []string { + depth int, index *index.LevelIndex, parentDataName string, messagerName string, keys []string) []string { cursor := len(keys) - if cursor >= len(descriptor.Fields) { + if cursor >= len(index.ColFields) { var keyParams string for i, key := range keys { keyParams += key @@ -130,13 +138,13 @@ func generateOneMulticolumnIndex(gen *protogen.Plugin, g *protogen.GeneratedFile keyParams += ", " } } - keyType := fmt.Sprintf("%s_Index_%sKey", descriptor.LevelMessage.FD.ContainingMessage().Name(), descriptor.Name) - indexContainerName := "index" + strcase.ToCamel(descriptor.Name) + "Map" + keyType := fmt.Sprintf("%s_Index_%sKey", messagerName, index.Name()) + indexContainerName := "index" + strcase.ToCamel(index.Name()) + "Map" g.P("key := ", keyType, " {", keyParams, "}") g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") return keys } - field := descriptor.Fields[cursor] + field := index.ColFields[cursor] if field.FD.IsList() { itemName := fmt.Sprintf("indexItem%d", cursor) fieldName := "" @@ -145,7 +153,7 @@ func generateOneMulticolumnIndex(gen *protogen.Plugin, g *protogen.GeneratedFile } g.P("for _, " + itemName + " := range " + parentDataName + fieldName + " {") keys = append(keys, itemName) - keys = generateOneMulticolumnIndex(gen, g, depth+1, descriptor, parentDataName, keys) + keys = generateOneMulticolumnIndex(gen, g, depth+1, index, parentDataName, messagerName, keys) g.P("}") } else { fieldName := "" @@ -154,56 +162,56 @@ func generateOneMulticolumnIndex(gen *protogen.Plugin, g *protogen.GeneratedFile } key := parentDataName + fieldName keys = append(keys, key) - keys = generateOneMulticolumnIndex(gen, g, depth, descriptor, parentDataName, keys) + keys = generateOneMulticolumnIndex(gen, g, depth, index, parentDataName, messagerName, keys) } return keys } -func genIndexFinders(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptors []*index.IndexDescriptor, messagerName string) { - for _, descriptor := range descriptors { - // sliceType := "[]*" + descriptor.GoIdent - mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, descriptor.Name) - indexContainerName := "index" + strcase.ToCamel(descriptor.Name) + "Map" +func genIndexFinders(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { + for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, index.Name()) + indexContainerName := "index" + strcase.ToCamel(index.Name()) + "Map" - g.P("// Index: ", descriptor.Index) - g.P() + g.P("// Index: ", index.Index) + g.P() - g.P("// Find", descriptor.Name, "Map returns the index(", descriptor.Index, ") to value(", helper.FindMessageGoIdent(gen, descriptor.MD), ") map.") - g.P("// One key may correspond to multiple values, which are contained by a slice.") - g.P("func (x *", messagerName, ") Find", descriptor.Name, "Map() ", mapType, " {") - g.P("return x.", indexContainerName) - g.P("}") - g.P() - - var keyType any - var keyName string - if len(descriptor.Fields) == 1 { - // single-column index - field := descriptor.Fields[0] // just take first field - keyType = helper.ParseGoType(gen, field.FD) - keyName = helper.ParseIndexFieldNameAsFuncParam(gen, field.FD) - } else { - // multi-column index - keyType = fmt.Sprintf("%s_Index_%sKey", descriptor.LevelMessage.FD.ContainingMessage().Name(), descriptor.Name) - keyName = "key" - } + g.P("// Find", index.Name(), "Map returns the index(", index.Index, ") to value(", helper.FindMessageGoIdent(gen, index.MD), ") map.") + g.P("// One key may correspond to multiple values, which are contained by a slice.") + g.P("func (x *", messagerName, ") Find", index.Name(), "Map() ", mapType, " {") + g.P("return x.", indexContainerName) + g.P("}") + g.P() - g.P("// Find", descriptor.Name, " returns a slice of all values of the given key.") - g.P("func (x *", messagerName, ") Find", descriptor.Name, "(", keyName, " ", keyType, ") []*", helper.FindMessageGoIdent(gen, descriptor.MD), " {") - g.P("return x.", indexContainerName, "[", keyName, "]") - g.P("}") - g.P() + var keyType any + var keyName string + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + keyType = helper.ParseGoType(gen, field.FD) + keyName = helper.ParseIndexFieldNameAsFuncParam(gen, field.FD) + } else { + // multi-column index + keyType = fmt.Sprintf("%s_Index_%sKey", messagerName, index.Name()) + keyName = "key" + } - g.P("// FindFirst", descriptor.Name, " returns the first value of the given key,") - g.P("// or nil if the key correspond to no value.") - g.P("func (x *", messagerName, ") FindFirst", descriptor.Name, "(", keyName, " ", keyType, ") *", helper.FindMessageGoIdent(gen, descriptor.MD), " {") - g.P("val := x.", indexContainerName, "[", keyName, "]") - g.P("if len(val) > 0 {") - g.P("return val[0]") - g.P("}") - g.P("return nil") - g.P("}") - g.P() + g.P("// Find", index.Name(), " returns a slice of all values of the given key.") + g.P("func (x *", messagerName, ") Find", index.Name(), "(", keyName, " ", keyType, ") []*", helper.FindMessageGoIdent(gen, index.MD), " {") + g.P("return x.", indexContainerName, "[", keyName, "]") + g.P("}") + g.P() + g.P("// FindFirst", index.Name(), " returns the first value of the given key,") + g.P("// or nil if the key correspond to no value.") + g.P("func (x *", messagerName, ") FindFirst", index.Name(), "(", keyName, " ", keyType, ") *", helper.FindMessageGoIdent(gen, index.MD), " {") + g.P("val := x.", indexContainerName, "[", keyName, "]") + g.P("if len(val) > 0 {") + g.P("return val[0]") + g.P("}") + g.P("return nil") + g.P("}") + g.P() + } } } diff --git a/cmd/protoc-gen-go-tableau-loader/messager.go b/cmd/protoc-gen-go-tableau-loader/messager.go index 442db51a..ce18b262 100644 --- a/cmd/protoc-gen-go-tableau-loader/messager.go +++ b/cmd/protoc-gen-go-tableau-loader/messager.go @@ -77,14 +77,14 @@ func generateRegister(messagers []string, g *protogen.GeneratedFile) { // genMessage generates a message definition. func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protogen.Message) { messagerName := string(message.Desc.Name()) - indexDescriptors := index.ParseIndexDescriptor(gen, message.Desc) + indexDescriptor := index.ParseIndexDescriptor(message.Desc) // type definitions if options.NeedGenOrderedMap(message.Desc, options.LangGO) { genOrderedMapTypeDef(gen, g, message.Desc, 1, nil, messagerName) } if options.NeedGenIndex(message.Desc, options.LangGO) { - genIndexTypeDef(gen, g, indexDescriptors, messagerName) + genIndexTypeDef(gen, g, indexDescriptor, messagerName) } g.P("// ", messagerName, " is a wrapper around protobuf message: ", message.GoIdent, ".") @@ -102,7 +102,7 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog genOrderedMapField(g, message.Desc) } if options.NeedGenIndex(message.Desc, options.LangGO) { - genIndexField(g, indexDescriptors, messagerName) + genIndexField(g, indexDescriptor, messagerName) } g.P("}") g.P() @@ -179,7 +179,7 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog genOrderedMapLoader(gen, g, message.Desc, 1, nil, messagerName, "") } if options.NeedGenIndex(message.Desc, options.LangGO) { - genIndexLoader(gen, g, indexDescriptors, messagerName) + genIndexLoader(gen, g, indexDescriptor, messagerName) } g.P("return nil") g.P("}") @@ -192,7 +192,7 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog genOrderedMapGetters(gen, g, message.Desc, 1, nil, messagerName) } if options.NeedGenIndex(message.Desc, options.LangGO) { - genIndexFinders(gen, g, indexDescriptors, messagerName) + genIndexFinders(gen, g, indexDescriptor, messagerName) } } diff --git a/go.mod b/go.mod index f1b8b696..fda23b6e 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 + github.com/stretchr/testify v1.10.0 github.com/tableauio/tableau v0.13.0 go.uber.org/zap v1.24.0 golang.org/x/exp v0.0.0-20230418202329-0354be287a23 @@ -20,6 +21,7 @@ require ( github.com/bytedance/sonic v1.13.2 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/cloudwego/base64x v0.1.5 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/jhump/protoreflect v1.16.0 // indirect @@ -31,7 +33,6 @@ require ( github.com/protocolbuffers/txtpbfmt v0.0.0-20240820135758-21b1d9897dc7 // indirect github.com/richardlehane/mscfb v1.0.4 // indirect github.com/richardlehane/msoleps v1.0.3 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/subchen/go-xmldom v1.1.2 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect diff --git a/go.sum b/go.sum index bd880e5c..af3c6908 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,6 @@ github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTK github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM= github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -107,11 +105,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subchen/go-xmldom v1.1.2 h1:7evI2YqfYYOnuj+PBwyaOZZYjl3iWq35P6KfBUw9jeU= github.com/subchen/go-xmldom v1.1.2/go.mod h1:6Pg/HuX5/T4Jlj0IPJF1sRxKVoI/rrKP6LIMge9d5/8= -github.com/tableauio/tableau v0.12.2 h1:1pTcMbR1a1fNRh8LIpnsRTcz2QzmmXR/JJJhry8pJ8E= -github.com/tableauio/tableau v0.12.2/go.mod h1:qc3iHJkkEdpRyAe7K0WxZ6Kosa50Al3kxZoJobwvT2w= github.com/tableauio/tableau v0.13.0 h1:IJje1CyCUtL6+GSF613ibmV+Aeh78j3mvDdx8rbAjI0= github.com/tableauio/tableau v0.13.0/go.mod h1:+gQn2d7unppJV5lepcSFdZyPmMWeWSzaf2bMd6SHT6U= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= @@ -175,7 +172,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index c1c64045..808abeb3 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -1,24 +1,16 @@ package index import ( + "fmt" "strings" "github.com/tableauio/tableau/proto/tableaupb" - "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/descriptorpb" ) type IndexDescriptor struct { - *Index - - MD protoreflect.MessageDescriptor // deepest level message descriptor - Name string // index name, e.g.: name of (ID, Name)@ItemInfo is "ItemInfo" - - Fields []*LevelField // index fields in the same struct (protobuf message), refer to the deepest level message's Fields. - KeyFields []*LevelField // key fields in the same struct (protobuf message), refer to the deepest level message's Fields. - LevelMessage *LevelMessage // message hierarchy to the deepest level message which contains all index fields. } @@ -54,59 +46,79 @@ type LevelField struct { type LevelMessage struct { NextLevel *LevelMessage - // Current level mesage's field which contains index fields. - // NOTE: FD, FieldName, and FieldCard are only valid when NextLevel is not nil. - FD protoreflect.FieldDescriptor // index field descriptor + // Current level message's field which contains index fields. + // Only valid when NextLevel is not nil. + FD protoreflect.FieldDescriptor - // Deepest level message fields corresponding to index fields - // NOTE: Fields is valid only when this level is the deepest level. - Fields []*LevelField + // Current level message's all index fields + Indexes []*LevelIndex +} - // Deepest level message fields corresponding to key fields - // NOTE: Fields is valid only when this level is the deepest level. +type LevelIndex struct { + *Index + MD protoreflect.MessageDescriptor + ColFields []*LevelField KeyFields []*LevelField } -// parseRecursively parses multi-column index related info. -func parseRecursively(gen *protogen.Plugin, cols, keys []string, prefix string, md protoreflect.MessageDescriptor) *LevelMessage { +func (l *LevelIndex) Name() string { + name := l.Index.Name + if name == "" { + name = string(l.MD.Name()) + } + return name +} + +func parseLevelMessage(md protoreflect.MessageDescriptor) *LevelMessage { levelInfo := &LevelMessage{} 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 + } else if fd.IsList() && fd.Kind() == protoreflect.MessageKind { + levelInfo.NextLevel = parseLevelMessage(fd.Message()) + levelInfo.FD = fd + return levelInfo + } + } + return &LevelMessage{} +} +// parseRecursively parses multi-column index related info. +func parseRecursively(index *Index, prefix string, md protoreflect.MessageDescriptor, levelMessage *LevelMessage) { + colFields := parseInSameLevel(index.Cols, prefix, md, nil) + keyFields := parseInSameLevel(index.Keys, prefix, md, nil) + if len(colFields) != 0 { + // index belongs to current level + levelMessage.Indexes = append(levelMessage.Indexes, &LevelIndex{ + Index: index, + MD: md, + ColFields: colFields, + KeyFields: keyFields, + }) + } else if levelMessage != nil && levelMessage.NextLevel != nil { + // index invalid or belongs to deeper level + fd := levelMessage.FD opts := fd.Options().(*descriptorpb.FieldOptions) fdOpts := proto.GetExtension(opts, tableaupb.E_Field).(*tableaupb.FieldOptions) fieldOptName := fdOpts.GetName() - if fd.IsMap() && fd.MapValue().Kind() == protoreflect.MessageKind { - levelInfo.NextLevel = parseRecursively(gen, cols, keys, prefix+fieldOptName, fd.MapValue().Message()) - if levelInfo.NextLevel != nil { - levelInfo.FD = fd - return levelInfo - } - } else if fd.IsList() && fd.Kind() == protoreflect.MessageKind { - levelInfo.NextLevel = parseRecursively(gen, cols, keys, prefix+fieldOptName, fd.Message()) - if levelInfo.NextLevel != nil { - levelInfo.FD = fd - return levelInfo - } + if fd.IsMap() { + parseRecursively(index, prefix+fieldOptName, fd.MapValue().Message(), levelMessage.NextLevel) + } else { + parseRecursively(index, prefix+fieldOptName, fd.Message(), levelMessage.NextLevel) } } - levelInfo.Fields = parseInSameLevel(gen, cols, prefix, md, nil) - levelInfo.KeyFields = parseInSameLevel(gen, keys, prefix, md, nil) - if len(levelInfo.Fields) != 0 { - return levelInfo - } - return nil } -func parseInSameLevel(gen *protogen.Plugin, cols []string, prefix string, md protoreflect.MessageDescriptor, leveledFDList []protoreflect.FieldDescriptor) []*LevelField { - levelFields := []*LevelField{} +func parseInSameLevel(cols []string, prefix string, md protoreflect.MessageDescriptor, leveledFDList []protoreflect.FieldDescriptor) []*LevelField { + var levelFields []*LevelField for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) - opts := fd.Options().(*descriptorpb.FieldOptions) fdOpts := proto.GetExtension(opts, tableaupb.E_Field).(*tableaupb.FieldOptions) fieldOptName := fdOpts.GetName() - for _, columnName := range cols { if prefix+fieldOptName == columnName { field := &LevelField{ @@ -115,10 +127,12 @@ func parseInSameLevel(gen *protogen.Plugin, cols []string, prefix string, md pro } levelFields = append(levelFields, field) break - } else if fd.Kind() == protoreflect.MessageKind && strings.HasPrefix(columnName, prefix+fieldOptName) { + } else if fd.Kind() == protoreflect.MessageKind && + !fd.IsMap() && !fd.IsList() && + strings.HasPrefix(columnName, prefix+fieldOptName) { levelFields = append(levelFields, parseInSameLevel( - gen, cols, prefix+fieldOptName, fd.Message(), + cols, prefix+fieldOptName, fd.Message(), append(leveledFDList, fd), )..., ) @@ -128,38 +142,29 @@ func parseInSameLevel(gen *protogen.Plugin, cols []string, prefix string, md pro return levelFields } -func ParseIndexDescriptor(gen *protogen.Plugin, md protoreflect.MessageDescriptor) []*IndexDescriptor { - descriptors := []*IndexDescriptor{} - indexes := parseWSOptionIndex(md) +func ParseIndexDescriptor(md protoreflect.MessageDescriptor) *IndexDescriptor { + descriptor := &IndexDescriptor{ + LevelMessage: parseLevelMessage(md), + } + indexes := ParseWSOptionIndex(md) + // parse indexes into level message for _, index := range indexes { if len(index.Cols) == 0 { continue } - levelInfo := parseRecursively(gen, index.Cols, index.Keys, "", md) - if levelInfo == nil { - continue - } - descriptor := &IndexDescriptor{ - Index: index, - LevelMessage: levelInfo, - } - deepestLevelMessage := descriptor.LevelMessage - for deepestLevelMessage.NextLevel != nil { - if fd := deepestLevelMessage.FD; fd.IsMap() { - descriptor.MD = fd.MapValue().Message() + parseRecursively(index, "", md, descriptor.LevelMessage) + } + // check duplicate index name + indexNameMap := map[string]*Index{} + for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + name := index.Name() + if existingIndex, ok := indexNameMap[name]; ok { + panic(fmt.Sprintf("duplicate index name on %v in %v: %v and %v", md.Name(), md.ParentFile().Path(), index, existingIndex)) } else { - descriptor.MD = fd.Message() + indexNameMap[name] = index.Index } - deepestLevelMessage = deepestLevelMessage.NextLevel - } - descriptor.Fields = deepestLevelMessage.Fields - descriptor.KeyFields = deepestLevelMessage.KeyFields - descriptor.Name = index.Name - if descriptor.Name == "" { - // use index field's parent message name if not set. - descriptor.Name = string(descriptor.MD.Name()) } - descriptors = append(descriptors, descriptor) } - return descriptors + return descriptor } diff --git a/internal/index/descriptor_test.go b/internal/index/descriptor_test.go new file mode 100644 index 00000000..365c83ea --- /dev/null +++ b/internal/index/descriptor_test.go @@ -0,0 +1,376 @@ +package index + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tableauio/loader/test/go-tableau-loader/protoconf" + "google.golang.org/protobuf/reflect/protoreflect" +) + +func Test_ParseIndexDescriptor(t *testing.T) { + type args struct { + md protoreflect.MessageDescriptor + } + tests := []struct { + name string + args args + want *IndexDescriptor + }{ + { + name: "ItemConf", + args: args{ + md: (&protoconf.ItemConf{}).ProtoReflect().Descriptor(), + }, + want: &IndexDescriptor{ + LevelMessage: &LevelMessage{ + FD: (&protoconf.ItemConf{}).ProtoReflect().Descriptor().Fields().ByName("item_map"), + NextLevel: &LevelMessage{ + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"Type"}, + Name: "", + }, + MD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("type"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("type"), + }, + }, + }, + }, + { + Index: &Index{ + Cols: []string{"Param"}, + Keys: []string{"ID"}, + Name: "ItemInfo", + }, + MD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("param_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("param_list"), + }, + }, + }, + KeyFields: []*LevelField{ + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("id"), + }, + }, + }, + }, + { + Index: &Index{ + Cols: []string{"Default"}, + Name: "ItemDefaultInfo", + }, + MD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("default"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("default"), + }, + }, + }, + }, + { + Index: &Index{ + Cols: []string{"ExtType"}, + Name: "ItemExtInfo", + }, + MD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("ext_type_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("ext_type_list"), + }, + }, + }, + }, + { + Index: &Index{ + Cols: []string{"ID", "Name"}, + Keys: []string{"Type", "UseEffectType"}, + Name: "AwardItem", + }, + MD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("id"), + }, + }, + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("name"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("name"), + }, + }, + }, + KeyFields: []*LevelField{ + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("type"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("type"), + }, + }, + { + FD: (&protoconf.UseEffect{}).ProtoReflect().Descriptor().Fields().ByName("type"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("use_effect"), + (&protoconf.UseEffect{}).ProtoReflect().Descriptor().Fields().ByName("type"), + }, + }, + }, + }, + { + Index: &Index{ + Cols: []string{"ID", "Type", "Param", "ExtType"}, + Name: "SpecialItem", + }, + MD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("id"), + }, + }, + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("type"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("type"), + }, + }, + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("param_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("param_list"), + }, + }, + { + FD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("ext_type_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("ext_type_list"), + }, + }, + }, + }, + { + Index: &Index{ + Cols: []string{"PathDir"}, + Name: "ItemPathDir", + }, + MD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.Path{}).ProtoReflect().Descriptor().Fields().ByName("dir"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("path"), + (&protoconf.Path{}).ProtoReflect().Descriptor().Fields().ByName("dir"), + }, + }, + }, + }, + { + Index: &Index{ + Cols: []string{"PathName"}, + Name: "ItemPathName", + }, + MD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.Path{}).ProtoReflect().Descriptor().Fields().ByName("name_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("path"), + (&protoconf.Path{}).ProtoReflect().Descriptor().Fields().ByName("name_list"), + }, + }, + }, + }, + { + Index: &Index{ + Cols: []string{"PathFriendID"}, + Name: "ItemPathFriendID", + }, + MD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.Path_Friend{}).ProtoReflect().Descriptor().Fields().ByName("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("path"), + (&protoconf.Path{}).ProtoReflect().Descriptor().Fields().ByName("friend"), + (&protoconf.Path_Friend{}).ProtoReflect().Descriptor().Fields().ByName("id"), + }, + }, + }, + }, + { + Index: &Index{ + Cols: []string{"UseEffectType"}, + Name: "UseEffectType", + }, + MD: (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.UseEffect{}).ProtoReflect().Descriptor().Fields().ByName("type"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ItemConf_Item{}).ProtoReflect().Descriptor().Fields().ByName("use_effect"), + (&protoconf.UseEffect{}).ProtoReflect().Descriptor().Fields().ByName("type"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "HeroConf", + args: args{ + md: (&protoconf.HeroConf{}).ProtoReflect().Descriptor(), + }, + want: &IndexDescriptor{ + LevelMessage: &LevelMessage{ + FD: (&protoconf.HeroConf{}).ProtoReflect().Descriptor().Fields().ByName("hero_map"), + NextLevel: &LevelMessage{ + FD: (&protoconf.HeroConf_Hero{}).ProtoReflect().Descriptor().Fields().ByName("attr_map"), + NextLevel: &LevelMessage{ + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"Title"}, + Name: "", + }, + MD: (&protoconf.HeroConf_Hero_Attr{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.HeroConf_Hero_Attr{}).ProtoReflect().Descriptor().Fields().ByName("title"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.HeroConf_Hero_Attr{}).ProtoReflect().Descriptor().Fields().ByName("title"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "ActivityConf", + args: args{ + md: (&protoconf.ActivityConf{}).ProtoReflect().Descriptor(), + }, + want: &IndexDescriptor{ + LevelMessage: &LevelMessage{ + FD: (&protoconf.ActivityConf{}).ProtoReflect().Descriptor().Fields().ByName("activity_map"), + NextLevel: &LevelMessage{ + FD: (&protoconf.ActivityConf_Activity{}).ProtoReflect().Descriptor().Fields().ByName("chapter_map"), + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"ActivityName"}, + Name: "", + }, + MD: (&protoconf.ActivityConf_Activity{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.ActivityConf_Activity{}).ProtoReflect().Descriptor().Fields().ByName("activity_name"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ActivityConf_Activity{}).ProtoReflect().Descriptor().Fields().ByName("activity_name"), + }, + }, + }, + }, + }, + NextLevel: &LevelMessage{ + FD: (&protoconf.ActivityConf_Activity_Chapter{}).ProtoReflect().Descriptor().Fields().ByName("section_map"), + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"ChapterID"}, + Name: "", + }, + MD: (&protoconf.ActivityConf_Activity_Chapter{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.ActivityConf_Activity_Chapter{}).ProtoReflect().Descriptor().Fields().ByName("chapter_id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ActivityConf_Activity_Chapter{}).ProtoReflect().Descriptor().Fields().ByName("chapter_id"), + }, + }, + }, + }, + { + Index: &Index{ + Cols: []string{"ChapterName"}, + Keys: []string{"AwardID"}, + Name: "NamedChapter", + }, + MD: (&protoconf.ActivityConf_Activity_Chapter{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.ActivityConf_Activity_Chapter{}).ProtoReflect().Descriptor().Fields().ByName("chapter_name"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ActivityConf_Activity_Chapter{}).ProtoReflect().Descriptor().Fields().ByName("chapter_name"), + }, + }, + }, + KeyFields: []*LevelField{ + { + FD: (&protoconf.ActivityConf_Activity_Chapter{}).ProtoReflect().Descriptor().Fields().ByName("award_id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.ActivityConf_Activity_Chapter{}).ProtoReflect().Descriptor().Fields().ByName("award_id"), + }, + }, + }, + }, + }, + NextLevel: &LevelMessage{ + FD: (&protoconf.Section{}).ProtoReflect().Descriptor().Fields().ByName("section_item_list"), + NextLevel: &LevelMessage{ + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"SectionItemID"}, + Name: "Award", + }, + MD: (&protoconf.Item{}).ProtoReflect().Descriptor(), + ColFields: []*LevelField{ + { + FD: (&protoconf.Item{}).ProtoReflect().Descriptor().Fields().ByName("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + (&protoconf.Item{}).ProtoReflect().Descriptor().Fields().ByName("id"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ParseIndexDescriptor(tt.args.md) + assert.EqualValues(t, tt.want, got) + }) + } +} diff --git a/internal/index/index.go b/internal/index/index.go index 350d10c3..b357c796 100644 --- a/internal/index/index.go +++ b/internal/index/index.go @@ -54,7 +54,7 @@ func (index *Index) String() string { } // parse worksheet option index -func parseWSOptionIndex(md protoreflect.MessageDescriptor) []*Index { +func ParseWSOptionIndex(md protoreflect.MessageDescriptor) []*Index { opts := md.Options().(*descriptorpb.MessageOptions) wsOpts := proto.GetExtension(opts, tableaupb.E_Worksheet).(*tableaupb.WorksheetOptions) return parseIndexFrom(wsOpts.Index) diff --git a/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc index 60cff677..a6428088 100644 --- a/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc @@ -22,6 +22,7 @@ bool HeroConf::Load(const std::string& dir, Format fmt, const LoadOptions* optio bool HeroConf::ProcessAfterLoad() { // OrderedMap init. + ordered_map_.clear(); for (auto&& item1 : data_.hero_map()) { ordered_map_[item1.first] = Hero_OrderedMapValue(Hero_Attr_OrderedMap(), &item1.second); auto&& ordered_map1 = ordered_map_[item1.first].first; 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 723e2599..98454bc4 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc @@ -22,76 +22,89 @@ bool ItemConf::Load(const std::string& dir, Format fmt, const LoadOptions* optio bool ItemConf::ProcessAfterLoad() { // OrderedMap init. + ordered_map_.clear(); for (auto&& item1 : data_.item_map()) { ordered_map_[item1.first] = &item1.second; } // Index init. - // Index: Type + index_item_map_.clear(); + index_item_info_map_.clear(); + index_item_default_info_map_.clear(); + index_item_ext_info_map_.clear(); + index_award_item_map_.clear(); + index_special_item_map_.clear(); + index_item_path_dir_map_.clear(); + index_item_path_name_map_.clear(); + index_item_path_friend_id_map_.clear(); + index_use_effect_type_map_.clear(); for (auto&& item1 : data_.item_map()) { - index_item_map_[item1.second.type()].push_back(&item1.second); - } - // Index: Param@ItemInfo - for (auto&& item1 : data_.item_map()) { - for (auto&& item2 : item1.second.param_list()) { - index_item_info_map_[item2].push_back(&item1.second); + { + // Index: Type + index_item_map_[item1.second.type()].push_back(&item1.second); } - } - 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(); - }); - } - // Index: Default@ItemDefaultInfo - for (auto&& item1 : data_.item_map()) { - index_item_default_info_map_[item1.second.default_()].push_back(&item1.second); - } - // Index: ExtType@ItemExtInfo - for (auto&& item1 : data_.item_map()) { - for (auto&& item2 : item1.second.ext_type_list()) { - index_item_ext_info_map_[static_cast(item2)].push_back(&item1.second); + { + // Index: Param@ItemInfo + for (auto&& item2 : item1.second.param_list()) { + index_item_info_map_[item2].push_back(&item1.second); + } + 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(); + }); + } } - } - // Index: (ID,Name)@AwardItem - for (auto&& item1 : data_.item_map()) { - Index_AwardItemKey key{item1.second.id(), item1.second.name()}; - index_award_item_map_[key].push_back(&item1.second); - } - 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(); - }); - } - // Index: (ID,Type,Param,ExtType)@SpecialItem - for (auto&& item1 : data_.item_map()) { - for (auto&& index_item2 : item1.second.param_list()) { - for (auto&& index_item3 : item1.second.ext_type_list()) { - Index_SpecialItemKey key{item1.second.id(), item1.second.type(), index_item2, static_cast(index_item3)}; - index_special_item_map_[key].push_back(&item1.second); + { + // Index: Default@ItemDefaultInfo + index_item_default_info_map_[item1.second.default_()].push_back(&item1.second); + } + { + // Index: ExtType@ItemExtInfo + for (auto&& item2 : item1.second.ext_type_list()) { + index_item_ext_info_map_[static_cast(item2)].push_back(&item1.second); } } - } - // Index: PathDir@ItemPathDir - for (auto&& item1 : data_.item_map()) { - index_item_path_dir_map_[item1.second.path().dir()].push_back(&item1.second); - } - // Index: PathName@ItemPathName - for (auto&& item1 : data_.item_map()) { - for (auto&& item2 : item1.second.path().name_list()) { - index_item_path_name_map_[item2].push_back(&item1.second); + { + // Index: (ID,Name)@AwardItem + Index_AwardItemKey key{item1.second.id(), item1.second.name()}; + index_award_item_map_[key].push_back(&item1.second); + 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(); + }); + } + } + { + // Index: (ID,Type,Param,ExtType)@SpecialItem + for (auto&& index_item2 : item1.second.param_list()) { + for (auto&& index_item3 : item1.second.ext_type_list()) { + Index_SpecialItemKey key{item1.second.id(), item1.second.type(), index_item2, static_cast(index_item3)}; + index_special_item_map_[key].push_back(&item1.second); + } + } + } + { + // Index: PathDir@ItemPathDir + index_item_path_dir_map_[item1.second.path().dir()].push_back(&item1.second); + } + { + // Index: PathName@ItemPathName + for (auto&& item2 : item1.second.path().name_list()) { + index_item_path_name_map_[item2].push_back(&item1.second); + } + } + { + // Index: PathFriendID@ItemPathFriendID + index_item_path_friend_id_map_[item1.second.path().friend_().id()].push_back(&item1.second); + } + { + // Index: UseEffectType@UseEffectType + index_use_effect_type_map_[item1.second.use_effect().type()].push_back(&item1.second); } - } - // Index: PathFriendID@ItemPathFriendID - for (auto&& item1 : data_.item_map()) { - index_item_path_friend_id_map_[item1.second.path().friend_().id()].push_back(&item1.second); - } - // Index: UseEffectType@UseEffectType - for (auto&& item1 : data_.item_map()) { - index_use_effect_type_map_[item1.second.use_effect().type()].push_back(&item1.second); } return true; } 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 014983bb..0ca93691 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc @@ -22,6 +22,7 @@ bool ActivityConf::Load(const std::string& dir, Format fmt, const LoadOptions* o bool ActivityConf::ProcessAfterLoad() { // OrderedMap init. + ordered_map_.clear(); for (auto&& item1 : data_.activity_map()) { ordered_map_[item1.first] = Activity_OrderedMapValue(Activity_Chapter_OrderedMap(), &item1.second); auto&& ordered_map1 = ordered_map_[item1.first].first; @@ -38,24 +39,40 @@ bool ActivityConf::ProcessAfterLoad() { } } // Index init. - // Index: ChapterID + index_activity_map_.clear(); + index_chapter_map_.clear(); + index_named_chapter_map_.clear(); + index_award_map_.clear(); for (auto&& item1 : data_.activity_map()) { - for (auto&& item2 : item1.second.chapter_map()) { - index_chapter_map_[item2.second.chapter_id()].push_back(&item2.second); + { + // Index: ActivityName + index_activity_map_[item1.second.activity_name()].push_back(&item1.second); } - } - // Index: ChapterName@NamedChapter - for (auto&& item1 : data_.activity_map()) { for (auto&& item2 : item1.second.chapter_map()) { - index_named_chapter_map_[item2.second.chapter_name()].push_back(&item2.second); + { + // Index: ChapterID + index_chapter_map_[item2.second.chapter_id()].push_back(&item2.second); + } + { + // Index: ChapterName@NamedChapter + index_named_chapter_map_[item2.second.chapter_name()].push_back(&item2.second); + 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(); + }); + } + } + for (auto&& item3 : item2.second.section_map()) { + for (auto&& item4 : item3.second.section_item_list()) { + { + // Index: SectionItemID@Award + index_award_map_[item4.id()].push_back(&item4); + } + } + } } } - 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(); - }); - } return true; } @@ -143,6 +160,25 @@ const ActivityConf::int32_OrderedMap* ActivityConf::GetOrderedMap(uint64_t activ return &iter->second.first; } +// Index: ActivityName +const ActivityConf::Index_ActivityMap& ActivityConf::FindActivity() 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); + if (iter == index_activity_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::ActivityConf::Activity* ActivityConf::FindFirstActivity(const std::string& activity_name) const { + auto conf = FindActivity(activity_name); + if (conf == nullptr || conf->size() == 0) { + return nullptr; + } + return (*conf)[0]; +} + // Index: ChapterID const ActivityConf::Index_ChapterMap& ActivityConf::FindChapter() const { return index_chapter_map_ ;} @@ -181,6 +217,25 @@ const protoconf::ActivityConf::Activity::Chapter* ActivityConf::FindFirstNamedCh return (*conf)[0]; } +// Index: SectionItemID@Award +const ActivityConf::Index_AwardMap& ActivityConf::FindAward() const { return index_award_map_ ;} + +const ActivityConf::Index_AwardVector* ActivityConf::FindAward(uint32_t id) const { + auto iter = index_award_map_.find(id); + if (iter == index_award_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Item* ActivityConf::FindFirstAward(uint32_t id) const { + auto conf = FindAward(id); + if (conf == nullptr || conf->size() == 0) { + return nullptr; + } + return (*conf)[0]; +} + const std::string ChapterConf::kProtoName = "ChapterConf"; 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 ee167d33..c45246eb 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h @@ -53,6 +53,17 @@ class ActivityConf : public Messager { Activity_OrderedMap ordered_map_; // Index accessers. + // Index: ActivityName + public: + using Index_ActivityVector = std::vector; + using Index_ActivityMap = std::unordered_map; + const Index_ActivityMap& FindActivity() const; + const Index_ActivityVector* FindActivity(const std::string& activity_name) const; + const protoconf::ActivityConf::Activity* FindFirstActivity(const std::string& activity_name) const; + + private: + Index_ActivityMap index_activity_map_; + // Index: ChapterID public: using Index_ChapterVector = std::vector; @@ -75,6 +86,17 @@ class ActivityConf : public Messager { private: Index_NamedChapterMap index_named_chapter_map_; + // Index: SectionItemID@Award + public: + using Index_AwardVector = std::vector; + using Index_AwardMap = std::unordered_map; + const Index_AwardMap& FindAward() const; + const Index_AwardVector* FindAward(uint32_t id) const; + const protoconf::Item* FindFirstAward(uint32_t id) const; + + private: + Index_AwardMap index_award_map_; + }; class ChapterConf : public Messager { 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 cc27c6cd..81f4291e 100644 --- a/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go @@ -97,12 +97,14 @@ func (x *HeroConf) originalMessage() proto.Message { // processAfterLoad runs after this messager is loaded. func (x *HeroConf) processAfterLoad() error { // Index init. - // Index: Title x.indexAttrMap = make(HeroConf_Index_AttrMap) for _, item1 := range x.data.GetHeroMap() { for _, item2 := range item1.GetAttrMap() { - key := item2.GetTitle() - x.indexAttrMap[key] = append(x.indexAttrMap[key], item2) + { + // Index: Title + key := item2.GetTitle() + x.indexAttrMap[key] = append(x.indexAttrMap[key], item2) + } } } return nil 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 9d1ec8da..b1ecb52c 100644 --- a/test/go-tableau-loader/protoconf/loader/item_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/item_conf.pc.go @@ -152,88 +152,90 @@ func (x *ItemConf) processAfterLoad() error { map1.Put(k1, v1) } // Index init. - // Index: Type x.indexItemMap = make(ItemConf_Index_ItemMap) - for _, item1 := range x.data.GetItemMap() { - key := item1.GetType() - x.indexItemMap[key] = append(x.indexItemMap[key], item1) - } - // Index: Param@ItemInfo x.indexItemInfoMap = make(ItemConf_Index_ItemInfoMap) - for _, item1 := range x.data.GetItemMap() { - for _, item2 := range item1.GetParamList() { - key := item2 - x.indexItemInfoMap[key] = append(x.indexItemInfoMap[key], item1) - } - } - for _, item := range x.indexItemInfoMap { - sort.Slice(item, func(i, j int) bool { - return item[i].GetId() < item[j].GetId() - }) - } - // Index: Default@ItemDefaultInfo x.indexItemDefaultInfoMap = make(ItemConf_Index_ItemDefaultInfoMap) - for _, item1 := range x.data.GetItemMap() { - key := item1.GetDefault() - x.indexItemDefaultInfoMap[key] = append(x.indexItemDefaultInfoMap[key], item1) - } - // Index: ExtType@ItemExtInfo x.indexItemExtInfoMap = make(ItemConf_Index_ItemExtInfoMap) - for _, item1 := range x.data.GetItemMap() { - for _, item2 := range item1.GetExtTypeList() { - key := item2 - x.indexItemExtInfoMap[key] = append(x.indexItemExtInfoMap[key], item1) - } - } - // Index: (ID,Name)@AwardItem x.indexAwardItemMap = make(ItemConf_Index_AwardItemMap) - for _, item1 := range x.data.GetItemMap() { - key := ItemConf_Index_AwardItemKey{item1.GetId(), item1.GetName()} - x.indexAwardItemMap[key] = append(x.indexAwardItemMap[key], item1) - } - 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() - } - return item[i].GetUseEffect().GetType() < item[j].GetUseEffect().GetType() - }) - } - // Index: (ID,Type,Param,ExtType)@SpecialItem x.indexSpecialItemMap = make(ItemConf_Index_SpecialItemMap) - for _, item1 := range x.data.GetItemMap() { - 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) - } - } - } - // Index: PathDir@ItemPathDir x.indexItemPathDirMap = make(ItemConf_Index_ItemPathDirMap) - for _, item1 := range x.data.GetItemMap() { - key := item1.GetPath().GetDir() - x.indexItemPathDirMap[key] = append(x.indexItemPathDirMap[key], item1) - } - // Index: PathName@ItemPathName x.indexItemPathNameMap = make(ItemConf_Index_ItemPathNameMap) - for _, item1 := range x.data.GetItemMap() { - for _, item2 := range item1.GetPath().GetNameList() { - key := item2 - x.indexItemPathNameMap[key] = append(x.indexItemPathNameMap[key], item1) - } - } - // Index: PathFriendID@ItemPathFriendID x.indexItemPathFriendIdMap = make(ItemConf_Index_ItemPathFriendIDMap) - for _, item1 := range x.data.GetItemMap() { - key := item1.GetPath().GetFriend().GetId() - x.indexItemPathFriendIdMap[key] = append(x.indexItemPathFriendIdMap[key], item1) - } - // Index: UseEffectType@UseEffectType x.indexUseEffectTypeMap = make(ItemConf_Index_UseEffectTypeMap) for _, item1 := range x.data.GetItemMap() { - key := item1.GetUseEffect().GetType() - x.indexUseEffectTypeMap[key] = append(x.indexUseEffectTypeMap[key], item1) + { + // Index: Type + key := item1.GetType() + x.indexItemMap[key] = append(x.indexItemMap[key], item1) + } + { + // Index: Param@ItemInfo + for _, item2 := range item1.GetParamList() { + key := item2 + x.indexItemInfoMap[key] = append(x.indexItemInfoMap[key], item1) + } + for _, item := range x.indexItemInfoMap { + sort.Slice(item, func(i, j int) bool { + return item[i].GetId() < item[j].GetId() + }) + } + } + { + // Index: Default@ItemDefaultInfo + key := item1.GetDefault() + x.indexItemDefaultInfoMap[key] = append(x.indexItemDefaultInfoMap[key], item1) + } + { + // Index: ExtType@ItemExtInfo + for _, item2 := range item1.GetExtTypeList() { + key := item2 + x.indexItemExtInfoMap[key] = append(x.indexItemExtInfoMap[key], item1) + } + } + { + // Index: (ID,Name)@AwardItem + key := ItemConf_Index_AwardItemKey{item1.GetId(), item1.GetName()} + x.indexAwardItemMap[key] = append(x.indexAwardItemMap[key], item1) + 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() + } + return item[i].GetUseEffect().GetType() < item[j].GetUseEffect().GetType() + }) + } + } + { + // 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) + } + } + } + { + // Index: PathDir@ItemPathDir + key := item1.GetPath().GetDir() + x.indexItemPathDirMap[key] = append(x.indexItemPathDirMap[key], item1) + } + { + // Index: PathName@ItemPathName + for _, item2 := range item1.GetPath().GetNameList() { + key := item2 + x.indexItemPathNameMap[key] = append(x.indexItemPathNameMap[key], item1) + } + } + { + // Index: PathFriendID@ItemPathFriendID + key := item1.GetPath().GetFriend().GetId() + x.indexItemPathFriendIdMap[key] = append(x.indexItemPathFriendIdMap[key], item1) + } + { + // Index: UseEffectType@UseEffectType + key := item1.GetUseEffect().GetType() + x.indexUseEffectTypeMap[key] = append(x.indexUseEffectTypeMap[key], item1) + } } return nil } 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 57425348..6c5460aa 100644 --- a/test/go-tableau-loader/protoconf/loader/test_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/test_conf.pc.go @@ -33,12 +33,18 @@ type ProtoconfActivityConfActivityMap_OrderedMapValue = pair.Pair[*ProtoconfActi type ProtoconfActivityConfActivityMap_OrderedMap = treemap.TreeMap[uint64, *ProtoconfActivityConfActivityMap_OrderedMapValue] // Index types. +// Index: ActivityName +type ActivityConf_Index_ActivityMap = map[string][]*protoconf.ActivityConf_Activity + // Index: ChapterID type ActivityConf_Index_ChapterMap = map[uint32][]*protoconf.ActivityConf_Activity_Chapter // Index: ChapterName@NamedChapter type ActivityConf_Index_NamedChapterMap = map[string][]*protoconf.ActivityConf_Activity_Chapter +// Index: SectionItemID@Award +type ActivityConf_Index_AwardMap = map[uint32][]*protoconf.Item + // ActivityConf is a wrapper around protobuf message: protoconf.ActivityConf. // // It is designed for three goals: @@ -50,8 +56,10 @@ type ActivityConf struct { UnimplementedMessager data, originalData *protoconf.ActivityConf orderedMap *ProtoconfActivityConfActivityMap_OrderedMap + indexActivityMap ActivityConf_Index_ActivityMap indexChapterMap ActivityConf_Index_ChapterMap indexNamedChapterMap ActivityConf_Index_NamedChapterMap + indexAwardMap ActivityConf_Index_AwardMap } // Name returns the ActivityConf's message name. @@ -144,27 +152,43 @@ func (x *ActivityConf) processAfterLoad() error { } } // Index init. - // Index: ChapterID + x.indexActivityMap = make(ActivityConf_Index_ActivityMap) x.indexChapterMap = make(ActivityConf_Index_ChapterMap) - for _, item1 := range x.data.GetActivityMap() { - for _, item2 := range item1.GetChapterMap() { - key := item2.GetChapterId() - x.indexChapterMap[key] = append(x.indexChapterMap[key], item2) - } - } - // Index: ChapterName@NamedChapter x.indexNamedChapterMap = make(ActivityConf_Index_NamedChapterMap) + x.indexAwardMap = make(ActivityConf_Index_AwardMap) for _, item1 := range x.data.GetActivityMap() { + { + // Index: ActivityName + key := item1.GetActivityName() + x.indexActivityMap[key] = append(x.indexActivityMap[key], item1) + } for _, item2 := range item1.GetChapterMap() { - key := item2.GetChapterName() - x.indexNamedChapterMap[key] = append(x.indexNamedChapterMap[key], item2) + { + // Index: ChapterID + key := item2.GetChapterId() + x.indexChapterMap[key] = append(x.indexChapterMap[key], item2) + } + { + // Index: ChapterName@NamedChapter + key := item2.GetChapterName() + x.indexNamedChapterMap[key] = append(x.indexNamedChapterMap[key], item2) + for _, item := range x.indexNamedChapterMap { + sort.Slice(item, func(i, j int) bool { + return item[i].GetAwardId() < item[j].GetAwardId() + }) + } + } + for _, item3 := range item2.GetSectionMap() { + for _, item4 := range item3.GetSectionItemList() { + { + // Index: SectionItemID@Award + key := item4.GetId() + x.indexAwardMap[key] = append(x.indexAwardMap[key], item4) + } + } + } } } - for _, item := range x.indexNamedChapterMap { - sort.Slice(item, func(i, j int) bool { - return item[i].GetAwardId() < item[j].GetAwardId() - }) - } return nil } @@ -268,6 +292,29 @@ func (x *ActivityConf) GetOrderedMap3(activityId uint64, chapterId uint32, secti } } +// Index: ActivityName + +// FindActivityMap returns the index(ActivityName) to value(protoconf.ActivityConf_Activity) map. +// One key may correspond to multiple values, which are contained by a slice. +func (x *ActivityConf) FindActivityMap() ActivityConf_Index_ActivityMap { + return x.indexActivityMap +} + +// FindActivity returns a slice of all values of the given key. +func (x *ActivityConf) FindActivity(activityName string) []*protoconf.ActivityConf_Activity { + return x.indexActivityMap[activityName] +} + +// FindFirstActivity returns the first value of the given key, +// or nil if the key correspond to no value. +func (x *ActivityConf) FindFirstActivity(activityName string) *protoconf.ActivityConf_Activity { + val := x.indexActivityMap[activityName] + if len(val) > 0 { + return val[0] + } + return nil +} + // Index: ChapterID // FindChapterMap returns the index(ChapterID) to value(protoconf.ActivityConf_Activity_Chapter) map. @@ -314,6 +361,29 @@ func (x *ActivityConf) FindFirstNamedChapter(chapterName string) *protoconf.Acti return nil } +// Index: SectionItemID@Award + +// FindAwardMap returns the index(SectionItemID@Award) to value(protoconf.Item) map. +// One key may correspond to multiple values, which are contained by a slice. +func (x *ActivityConf) FindAwardMap() ActivityConf_Index_AwardMap { + return x.indexAwardMap +} + +// FindAward returns a slice of all values of the given key. +func (x *ActivityConf) FindAward(id uint32) []*protoconf.Item { + return x.indexAwardMap[id] +} + +// FindFirstAward returns the first value of the given key, +// or nil if the key correspond to no value. +func (x *ActivityConf) FindFirstAward(id uint32) *protoconf.Item { + val := x.indexAwardMap[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: diff --git a/test/proto/test_conf.proto b/test/proto/test_conf.proto index 7cedbb8b..e10950da 100644 --- a/test/proto/test_conf.proto +++ b/test/proto/test_conf.proto @@ -14,9 +14,10 @@ message ActivityConf { option (tableau.worksheet) = { name: "Activity" ordered_map: true + index: "ActivityName" index: "ChapterID" index: "ChapterName@NamedChapter" - index: "SectionItemId@Award" + index: "SectionItemID@Award" }; map activity_map = 1 [(tableau.field) = { key: "ActivityID" layout: LAYOUT_VERTICAL }];