From d097c43f9bb72285c7a4c095370786e3d17f9328 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Fri, 9 May 2025 20:59:00 +0800 Subject: [PATCH 1/8] feat: panic on duplicate index names --- internal/index/descriptor.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index c1c64045..e8ed5f6a 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -1,6 +1,7 @@ package index import ( + "fmt" "strings" "github.com/tableauio/tableau/proto/tableaupb" @@ -161,5 +162,14 @@ func ParseIndexDescriptor(gen *protogen.Plugin, md protoreflect.MessageDescripto } descriptors = append(descriptors, descriptor) } + // check duplicate index name + indexNameMap := map[string]int{} + for i, descriptor := range descriptors { + if j, ok := indexNameMap[descriptor.Name]; ok { + panic(fmt.Sprintf("duplicate index name: %v and %v", indexes[j], indexes[i])) + } else { + indexNameMap[descriptor.Name] = i + } + } return descriptors } From 9bddffff3e03de15e49be4b5e5baffa020c6e614 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Sat, 10 May 2025 14:25:46 +0800 Subject: [PATCH 2/8] feat: add message name on panic --- internal/index/descriptor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index e8ed5f6a..91e15656 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -166,7 +166,7 @@ func ParseIndexDescriptor(gen *protogen.Plugin, md protoreflect.MessageDescripto indexNameMap := map[string]int{} for i, descriptor := range descriptors { if j, ok := indexNameMap[descriptor.Name]; ok { - panic(fmt.Sprintf("duplicate index name: %v and %v", indexes[j], indexes[i])) + panic(fmt.Sprintf("duplicate index name on %v: %v and %v", md.Name(), indexes[j], indexes[i])) } else { indexNameMap[descriptor.Name] = i } From 478beeb2a80897ec44b0b3fc790d0062a349f391 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Sat, 10 May 2025 14:28:49 +0800 Subject: [PATCH 3/8] feat: add file path --- internal/index/descriptor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index 91e15656..1ee9716b 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -166,7 +166,7 @@ func ParseIndexDescriptor(gen *protogen.Plugin, md protoreflect.MessageDescripto indexNameMap := map[string]int{} for i, descriptor := range descriptors { if j, ok := indexNameMap[descriptor.Name]; ok { - panic(fmt.Sprintf("duplicate index name on %v: %v and %v", md.Name(), indexes[j], indexes[i])) + panic(fmt.Sprintf("duplicate index name on %v in %v: %v and %v", md.Name(), md.ParentFile().Path(), indexes[j], indexes[i])) } else { indexNameMap[descriptor.Name] = i } From 49116c355bf74b508bb391f000bc478b924b89eb Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Tue, 20 May 2025 15:24:17 +0800 Subject: [PATCH 4/8] feat(cpp): clear ordered map and index data first in ProcessAfterLoad --- cmd/protoc-gen-cpp-tableau-loader/index.go | 3 +++ cmd/protoc-gen-cpp-tableau-loader/ordered_map.go | 1 + test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc | 1 + test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc | 11 +++++++++++ test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc | 3 +++ 5 files changed, 19 insertions(+) diff --git a/cmd/protoc-gen-cpp-tableau-loader/index.go b/cmd/protoc-gen-cpp-tableau-loader/index.go index 112826bb..f60bc359 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/index.go @@ -100,6 +100,9 @@ func genOneCppIndexLoader(g *protogen.GeneratedFile, depth int, descriptor *inde return } indexContainerName := "index_" + strcase.ToSnake(descriptor.Name) + "_map_" + if depth == 1 { + g.P(" ", indexContainerName, ".clear();") + } if levelMessage.NextLevel != nil { itemName := fmt.Sprintf("item%d", depth) g.P(strings.Repeat(" ", depth), "for (auto&& "+itemName+" : "+parentDataName+"."+helper.ParseIndexFieldName(levelMessage.FD)+"()) {") 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/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..c258c012 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc @@ -22,15 +22,18 @@ 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(); for (auto&& item1 : data_.item_map()) { index_item_map_[item1.second.type()].push_back(&item1.second); } // Index: Param@ItemInfo + index_item_info_map_.clear(); for (auto&& item1 : data_.item_map()) { for (auto&& item2 : item1.second.param_list()) { index_item_info_map_[item2].push_back(&item1.second); @@ -43,16 +46,19 @@ bool ItemConf::ProcessAfterLoad() { }); } // Index: Default@ItemDefaultInfo + index_item_default_info_map_.clear(); for (auto&& item1 : data_.item_map()) { index_item_default_info_map_[item1.second.default_()].push_back(&item1.second); } // Index: ExtType@ItemExtInfo + index_item_ext_info_map_.clear(); 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: (ID,Name)@AwardItem + index_award_item_map_.clear(); for (auto&& item1 : data_.item_map()) { Index_AwardItemKey key{item1.second.id(), item1.second.name()}; index_award_item_map_[key].push_back(&item1.second); @@ -67,6 +73,7 @@ bool ItemConf::ProcessAfterLoad() { }); } // Index: (ID,Type,Param,ExtType)@SpecialItem + index_special_item_map_.clear(); for (auto&& item1 : data_.item_map()) { for (auto&& index_item2 : item1.second.param_list()) { for (auto&& index_item3 : item1.second.ext_type_list()) { @@ -76,20 +83,24 @@ bool ItemConf::ProcessAfterLoad() { } } // Index: PathDir@ItemPathDir + index_item_path_dir_map_.clear(); for (auto&& item1 : data_.item_map()) { index_item_path_dir_map_[item1.second.path().dir()].push_back(&item1.second); } // Index: PathName@ItemPathName + index_item_path_name_map_.clear(); 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: PathFriendID@ItemPathFriendID + index_item_path_friend_id_map_.clear(); for (auto&& item1 : data_.item_map()) { index_item_path_friend_id_map_[item1.second.path().friend_().id()].push_back(&item1.second); } // Index: UseEffectType@UseEffectType + index_use_effect_type_map_.clear(); for (auto&& item1 : data_.item_map()) { index_use_effect_type_map_[item1.second.use_effect().type()].push_back(&item1.second); } 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..c50e4136 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; @@ -39,12 +40,14 @@ bool ActivityConf::ProcessAfterLoad() { } // Index init. // Index: ChapterID + index_chapter_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: ChapterName@NamedChapter + index_named_chapter_map_.clear(); 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); From 5f849406ff3b952637792de691201a1a9cd324d7 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Tue, 20 May 2025 19:37:18 +0800 Subject: [PATCH 5/8] feat: update test proto --- .../src/protoconf/test_conf.pc.cc | 54 ++++++++++++++ .../src/protoconf/test_conf.pc.h | 22 ++++++ .../protoconf/loader/test_conf.pc.go | 72 +++++++++++++++++++ test/proto/test_conf.proto | 3 +- 4 files changed, 150 insertions(+), 1 deletion(-) 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 c50e4136..08071718 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc @@ -39,6 +39,11 @@ bool ActivityConf::ProcessAfterLoad() { } } // Index init. + // Index: ActivityName + index_activity_map_.clear(); + for (auto&& item1 : data_.activity_map()) { + index_activity_map_[item1.second.activity_name()].push_back(&item1.second); + } // Index: ChapterID index_chapter_map_.clear(); for (auto&& item1 : data_.activity_map()) { @@ -59,6 +64,17 @@ bool ActivityConf::ProcessAfterLoad() { return a->award_id() < b->award_id(); }); } + // Index: SectionItemID@Award + index_award_map_.clear(); + for (auto&& item1 : data_.activity_map()) { + for (auto&& item2 : item1.second.chapter_map()) { + for (auto&& item3 : item2.second.section_map()) { + for (auto&& item4 : item3.second.section_item_list()) { + index_award_map_[item4.id()].push_back(&item4); + } + } + } + } return true; } @@ -146,6 +162,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_ ;} @@ -184,6 +219,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/test_conf.pc.go b/test/go-tableau-loader/protoconf/loader/test_conf.pc.go index 57425348..a0bca9c0 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,6 +152,12 @@ func (x *ActivityConf) processAfterLoad() error { } } // Index init. + // Index: ActivityName + x.indexActivityMap = make(ActivityConf_Index_ActivityMap) + for _, item1 := range x.data.GetActivityMap() { + key := item1.GetActivityName() + x.indexActivityMap[key] = append(x.indexActivityMap[key], item1) + } // Index: ChapterID x.indexChapterMap = make(ActivityConf_Index_ChapterMap) for _, item1 := range x.data.GetActivityMap() { @@ -165,6 +179,18 @@ func (x *ActivityConf) processAfterLoad() error { return item[i].GetAwardId() < item[j].GetAwardId() }) } + // Index: SectionItemID@Award + x.indexAwardMap = make(ActivityConf_Index_AwardMap) + for _, item1 := range x.data.GetActivityMap() { + for _, item2 := range item1.GetChapterMap() { + for _, item3 := range item2.GetSectionMap() { + for _, item4 := range item3.GetSectionItemList() { + key := item4.GetId() + x.indexAwardMap[key] = append(x.indexAwardMap[key], item4) + } + } + } + } return nil } @@ -268,6 +294,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 +363,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 }]; From 1ee3c6f10539e407367f586e962e6d2821855cf4 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Tue, 20 May 2025 21:11:25 +0800 Subject: [PATCH 6/8] feat: go index --- cmd/protoc-gen-go-tableau-loader/index.go | 252 ++++++------ cmd/protoc-gen-go-tableau-loader/messager.go | 12 +- go.mod | 3 +- go.sum | 8 +- internal/index/desc/desc.go | 171 ++++++++ internal/index/desc/desc_test.go | 377 ++++++++++++++++++ internal/index/descriptor.go | 2 +- internal/index/index.go | 2 +- .../protoconf/loader/hero_conf.pc.go | 8 +- .../protoconf/loader/item_conf.pc.go | 144 +++---- .../protoconf/loader/test_conf.pc.go | 52 ++- 11 files changed, 793 insertions(+), 238 deletions(-) create mode 100644 internal/index/desc/desc.go create mode 100644 internal/index/desc/desc_test.go diff --git a/cmd/protoc-gen-go-tableau-loader/index.go b/cmd/protoc-gen-go-tableau-loader/index.go index b5e32833..580d2937 100644 --- a/cmd/protoc-gen-go-tableau-loader/index.go +++ b/cmd/protoc-gen-go-tableau-loader/index.go @@ -5,108 +5,115 @@ import ( "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/index/desc" "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 *desc.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 *desc.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 *desc.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, ")") + depth++ + defer g.P("}") + } +} + +func genOneIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, depth int, index *desc.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 *desc.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 *desc.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..360e328d 100644 --- a/cmd/protoc-gen-go-tableau-loader/messager.go +++ b/cmd/protoc-gen-go-tableau-loader/messager.go @@ -5,7 +5,7 @@ import ( "path/filepath" "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" - "github.com/tableauio/loader/internal/index" + "github.com/tableauio/loader/internal/index/desc" "github.com/tableauio/loader/internal/options" "github.com/tableauio/tableau/proto/tableaupb" "google.golang.org/protobuf/compiler/protogen" @@ -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 := desc.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/desc/desc.go b/internal/index/desc/desc.go new file mode 100644 index 00000000..daf2b691 --- /dev/null +++ b/internal/index/desc/desc.go @@ -0,0 +1,171 @@ +package desc + +import ( + "fmt" + "strings" + + "github.com/tableauio/loader/internal/index" + "github.com/tableauio/tableau/proto/tableaupb" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" +) + +type IndexDescriptor struct { + LevelMessage *LevelMessage // message hierarchy to the deepest level message which contains all index fields. +} + +type LevelField struct { + FD protoreflect.FieldDescriptor // index field descriptor + + // leveled fd list + // For example, if you have a message described as below and created an index on "PathUserID" + // fds are ["path", "user", "id"] + // + // message ItemConf { + // option (tableau.worksheet) = { + // name: "ItemConf" + // index: "PathUserID" + // }; + // map item_map = 1 [(tableau.field) = { key: "ID" layout: LAYOUT_VERTICAL }]; + // message Item { + // uint32 id = 1 [(tableau.field) = { name: "ID" }]; + // Path path = 2 [(tableau.field) = { name: "Path" }]; + // message Path { + // string dir = 1 [(tableau.field) = { name: "Dir" }]; + // User user = 2 [(tableau.field) = { name: "User" }]; + // message User { + // uint32 id = 1 [(tableau.field) = { name: "ID" }]; + // } + // } + // } + // } + LeveledFDList []protoreflect.FieldDescriptor +} + +// namespaced level info +type LevelMessage struct { + NextLevel *LevelMessage + + // Current level message's field which contains index fields. + // Only valid when NextLevel is not nil. + FD protoreflect.FieldDescriptor + + // Current level message's all index fields + Indexes []*LevelIndex +} + +type LevelIndex struct { + *index.Index + MD protoreflect.MessageDescriptor + ColFields []*LevelField + KeyFields []*LevelField +} + +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.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() { + parseRecursively(index, prefix+fieldOptName, fd.MapValue().Message(), levelMessage.NextLevel) + } else { + parseRecursively(index, prefix+fieldOptName, fd.Message(), levelMessage.NextLevel) + } + } +} + +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{ + FD: fd, + LeveledFDList: append(leveledFDList, fd), + } + levelFields = append(levelFields, field) + break + } else if fd.Kind() == protoreflect.MessageKind && + !fd.IsMap() && !fd.IsList() && + strings.HasPrefix(columnName, prefix+fieldOptName) { + levelFields = append(levelFields, + parseInSameLevel( + cols, prefix+fieldOptName, fd.Message(), + append(leveledFDList, fd), + )..., + ) + } + } + } + return levelFields +} + +func ParseIndexDescriptor(md protoreflect.MessageDescriptor) *IndexDescriptor { + descriptor := &IndexDescriptor{ + LevelMessage: parseLevelMessage(md), + } + indexes := index.ParseWSOptionIndex(md) + // parse indexes into level message + for _, index := range indexes { + if len(index.Cols) == 0 { + continue + } + parseRecursively(index, "", md, descriptor.LevelMessage) + } + // check duplicate index name + indexNameMap := map[string]*index.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 { + indexNameMap[name] = index.Index + } + } + } + return descriptor +} diff --git a/internal/index/desc/desc_test.go b/internal/index/desc/desc_test.go new file mode 100644 index 00000000..4a5be062 --- /dev/null +++ b/internal/index/desc/desc_test.go @@ -0,0 +1,377 @@ +package desc + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tableauio/loader/internal/index" + "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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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/descriptor.go b/internal/index/descriptor.go index 1ee9716b..3934d6e5 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -131,7 +131,7 @@ func parseInSameLevel(gen *protogen.Plugin, cols []string, prefix string, md pro func ParseIndexDescriptor(gen *protogen.Plugin, md protoreflect.MessageDescriptor) []*IndexDescriptor { descriptors := []*IndexDescriptor{} - indexes := parseWSOptionIndex(md) + indexes := ParseWSOptionIndex(md) for _, index := range indexes { if len(index.Cols) == 0 { continue 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/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 a0bca9c0..6c5460aa 100644 --- a/test/go-tableau-loader/protoconf/loader/test_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/test_conf.pc.go @@ -152,41 +152,39 @@ func (x *ActivityConf) processAfterLoad() error { } } // Index init. - // Index: ActivityName x.indexActivityMap = make(ActivityConf_Index_ActivityMap) - for _, item1 := range x.data.GetActivityMap() { - key := item1.GetActivityName() - x.indexActivityMap[key] = append(x.indexActivityMap[key], item1) - } - // Index: ChapterID 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) - for _, item1 := range x.data.GetActivityMap() { - for _, item2 := range item1.GetChapterMap() { - 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() - }) - } - // Index: SectionItemID@Award 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() { + { + // 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() { - key := item4.GetId() - x.indexAwardMap[key] = append(x.indexAwardMap[key], item4) + { + // Index: SectionItemID@Award + key := item4.GetId() + x.indexAwardMap[key] = append(x.indexAwardMap[key], item4) + } } } } From 1e1a593b01d20db2933b75d9167e0a95a583e446 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Tue, 20 May 2025 22:17:43 +0800 Subject: [PATCH 7/8] feat: cpp --- cmd/protoc-gen-cpp-tableau-loader/index.go | 346 +++++++++--------- cmd/protoc-gen-cpp-tableau-loader/messager.go | 12 +- cmd/protoc-gen-go-tableau-loader/index.go | 2 +- .../src/protoconf/item_conf.pc.cc | 132 +++---- .../src/protoconf/test_conf.pc.cc | 46 ++- 5 files changed, 272 insertions(+), 266 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/index.go b/cmd/protoc-gen-cpp-tableau-loader/index.go index f60bc359..5228a0c8 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/index.go @@ -6,149 +6,153 @@ import ( "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/index/desc" "google.golang.org/protobuf/compiler/protogen" ) -func genHppIndexFinders(g *protogen.GeneratedFile, descriptors []*index.IndexDescriptor) { +func genHppIndexFinders(g *protogen.GeneratedFile, descriptor *desc.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) + 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 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 += " && " + // 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(" };") + 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 += ", " + // 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(" 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(" 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(descriptor.Name) + "_map_" - g.P(" ", mapType, " ", indexContainerName, ";") - 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 *desc.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 - } - indexContainerName := "index_" + strcase.ToSnake(descriptor.Name) + "_map_" - if depth == 1 { - g.P(" ", indexContainerName, ".clear();") + 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();") + } } - 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 *desc.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 := "." @@ -157,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 *desc.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 @@ -180,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 { @@ -208,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() +func genCppIndexFinders(g *protogen.GeneratedFile, descriptor *desc.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_" - 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" - } + g.P("// Index: ", index.Index) + g.P("const ", messagerName, "::", mapType, "& "+messagerName+"::Find", index.Name(), "() const { return "+indexContainerName+" ;}") + g.P() - 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() + 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 ", 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("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..a4a80608 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/messager.go +++ b/cmd/protoc-gen-cpp-tableau-loader/messager.go @@ -4,7 +4,7 @@ import ( "strings" "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" - "github.com/tableauio/loader/internal/index" + "github.com/tableauio/loader/internal/index/desc" "github.com/tableauio/loader/internal/options" "github.com/tableauio/tableau/proto/tableaupb" "google.golang.org/protobuf/compiler/protogen" @@ -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 := desc.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 := desc.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-go-tableau-loader/index.go b/cmd/protoc-gen-go-tableau-loader/index.go index 580d2937..12bac902 100644 --- a/cmd/protoc-gen-go-tableau-loader/index.go +++ b/cmd/protoc-gen-go-tableau-loader/index.go @@ -70,8 +70,8 @@ func genIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor } g.P("for _, ", itemName, " := range "+parentDataName+".Get"+helper.ParseIndexFieldName(gen, levelMessage.FD)+"() {") parentDataName = itemName - depth++ defer g.P("}") + depth++ } } 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 c258c012..98454bc4 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc @@ -27,82 +27,84 @@ bool ItemConf::ProcessAfterLoad() { ordered_map_[item1.first] = &item1.second; } // Index init. - // Index: Type index_item_map_.clear(); - for (auto&& item1 : data_.item_map()) { - index_item_map_[item1.second.type()].push_back(&item1.second); - } - // Index: Param@ItemInfo index_item_info_map_.clear(); - for (auto&& item1 : data_.item_map()) { - 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: Default@ItemDefaultInfo index_item_default_info_map_.clear(); - for (auto&& item1 : data_.item_map()) { - index_item_default_info_map_[item1.second.default_()].push_back(&item1.second); - } - // Index: ExtType@ItemExtInfo index_item_ext_info_map_.clear(); - 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: (ID,Name)@AwardItem index_award_item_map_.clear(); - 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 index_special_item_map_.clear(); - 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: PathDir@ItemPathDir index_item_path_dir_map_.clear(); - for (auto&& item1 : data_.item_map()) { - index_item_path_dir_map_[item1.second.path().dir()].push_back(&item1.second); - } - // Index: PathName@ItemPathName index_item_path_name_map_.clear(); - 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: PathFriendID@ItemPathFriendID index_item_path_friend_id_map_.clear(); - for (auto&& item1 : data_.item_map()) { - index_item_path_friend_id_map_[item1.second.path().friend_().id()].push_back(&item1.second); - } - // Index: UseEffectType@UseEffectType index_use_effect_type_map_.clear(); for (auto&& item1 : data_.item_map()) { - index_use_effect_type_map_[item1.second.use_effect().type()].push_back(&item1.second); + { + // Index: Type + index_item_map_[item1.second.type()].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: 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: (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); + } } 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 08071718..0ca93691 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc @@ -39,38 +39,36 @@ bool ActivityConf::ProcessAfterLoad() { } } // Index init. - // Index: ActivityName index_activity_map_.clear(); - for (auto&& item1 : data_.activity_map()) { - index_activity_map_[item1.second.activity_name()].push_back(&item1.second); - } - // Index: ChapterID index_chapter_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: ChapterName@NamedChapter index_named_chapter_map_.clear(); - 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); - } - } - 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(); - }); - } - // Index: SectionItemID@Award index_award_map_.clear(); for (auto&& item1 : data_.activity_map()) { + { + // Index: ActivityName + index_activity_map_[item1.second.activity_name()].push_back(&item1.second); + } for (auto&& item2 : item1.second.chapter_map()) { + { + // 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_award_map_[item4.id()].push_back(&item4); + { + // Index: SectionItemID@Award + index_award_map_[item4.id()].push_back(&item4); + } } } } From 340bc9de488bcf02e3eadb15d4a7c14b2417adc1 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Tue, 20 May 2025 22:24:48 +0800 Subject: [PATCH 8/8] feat: dir --- cmd/protoc-gen-cpp-tableau-loader/index.go | 12 +- cmd/protoc-gen-cpp-tableau-loader/messager.go | 6 +- cmd/protoc-gen-go-tableau-loader/index.go | 14 +- cmd/protoc-gen-go-tableau-loader/messager.go | 4 +- internal/index/desc/desc.go | 171 ------------------ internal/index/descriptor.go | 149 ++++++++------- .../{desc/desc_test.go => descriptor_test.go} | 33 ++-- 7 files changed, 106 insertions(+), 283 deletions(-) delete mode 100644 internal/index/desc/desc.go rename internal/index/{desc/desc_test.go => descriptor_test.go} (96%) diff --git a/cmd/protoc-gen-cpp-tableau-loader/index.go b/cmd/protoc-gen-cpp-tableau-loader/index.go index 5228a0c8..ce9f408d 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/index.go @@ -6,11 +6,11 @@ import ( "github.com/iancoleman/strcase" "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" - "github.com/tableauio/loader/internal/index/desc" + "github.com/tableauio/loader/internal/index" "google.golang.org/protobuf/compiler/protogen" ) -func genHppIndexFinders(g *protogen.GeneratedFile, descriptor *desc.IndexDescriptor) { +func genHppIndexFinders(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { g.P(" // Index accessers.") for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { @@ -88,7 +88,7 @@ func genHppIndexFinders(g *protogen.GeneratedFile, descriptor *desc.IndexDescrip } } -func genCppIndexLoader(g *protogen.GeneratedFile, descriptor *desc.IndexDescriptor) { +func genCppIndexLoader(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { g.P(" // Index init.") for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { @@ -116,7 +116,7 @@ func genCppIndexLoader(g *protogen.GeneratedFile, descriptor *desc.IndexDescript } } -func genOneCppIndexLoader(g *protogen.GeneratedFile, depth int, index *desc.LevelIndex, parentDataName string) { +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) @@ -175,7 +175,7 @@ func genOneCppIndexLoader(g *protogen.GeneratedFile, depth int, index *desc.Leve g.P(strings.Repeat(" ", depth), "}") } -func generateOneCppMulticolumnIndex(g *protogen.GeneratedFile, depth int, index *desc.LevelIndex, 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(index.ColFields) { var keyParams string @@ -218,7 +218,7 @@ func generateOneCppMulticolumnIndex(g *protogen.GeneratedFile, depth int, index return keys } -func genCppIndexFinders(g *protogen.GeneratedFile, descriptor *desc.IndexDescriptor, messagerName string) { +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" diff --git a/cmd/protoc-gen-cpp-tableau-loader/messager.go b/cmd/protoc-gen-cpp-tableau-loader/messager.go index a4a80608..4a709add 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/messager.go +++ b/cmd/protoc-gen-cpp-tableau-loader/messager.go @@ -4,7 +4,7 @@ import ( "strings" "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" - "github.com/tableauio/loader/internal/index/desc" + "github.com/tableauio/loader/internal/index" "github.com/tableauio/loader/internal/options" "github.com/tableauio/tableau/proto/tableaupb" "google.golang.org/protobuf/compiler/protogen" @@ -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()) - indexDescriptor := desc.ParseIndexDescriptor(message.Desc) + indexDescriptor := index.ParseIndexDescriptor(message.Desc) g.P("class ", message.Desc.Name(), " : public Messager {") g.P(" public:") @@ -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()) - indexDescriptor := desc.ParseIndexDescriptor(message.Desc) + indexDescriptor := index.ParseIndexDescriptor(message.Desc) g.P("const std::string ", messagerName, "::kProtoName = ", `"`, messagerName, `";`) g.P() diff --git a/cmd/protoc-gen-go-tableau-loader/index.go b/cmd/protoc-gen-go-tableau-loader/index.go index 12bac902..e61af74b 100644 --- a/cmd/protoc-gen-go-tableau-loader/index.go +++ b/cmd/protoc-gen-go-tableau-loader/index.go @@ -5,11 +5,11 @@ import ( "github.com/iancoleman/strcase" "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" - "github.com/tableauio/loader/internal/index/desc" + "github.com/tableauio/loader/internal/index" "google.golang.org/protobuf/compiler/protogen" ) -func genIndexTypeDef(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *desc.IndexDescriptor, messagerName string) { +func genIndexTypeDef(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { g.P("// Index types.") for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { @@ -40,7 +40,7 @@ func genIndexTypeDef(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor } } -func genIndexField(g *protogen.GeneratedFile, descriptor *desc.IndexDescriptor, messagerName string) { +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" @@ -50,7 +50,7 @@ func genIndexField(g *protogen.GeneratedFile, descriptor *desc.IndexDescriptor, } } -func genIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *desc.IndexDescriptor, messagerName string) { +func genIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { g.P(" // Index init.") for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { @@ -75,7 +75,7 @@ func genIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor } } -func genOneIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, depth int, index *desc.LevelIndex, +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("{") @@ -128,7 +128,7 @@ func genOneIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, depth in } func generateOneMulticolumnIndex(gen *protogen.Plugin, g *protogen.GeneratedFile, - depth int, index *desc.LevelIndex, parentDataName string, messagerName string, keys []string) []string { + depth int, index *index.LevelIndex, parentDataName string, messagerName string, keys []string) []string { cursor := len(keys) if cursor >= len(index.ColFields) { var keyParams string @@ -167,7 +167,7 @@ func generateOneMulticolumnIndex(gen *protogen.Plugin, g *protogen.GeneratedFile return keys } -func genIndexFinders(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *desc.IndexDescriptor, messagerName string) { +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()) diff --git a/cmd/protoc-gen-go-tableau-loader/messager.go b/cmd/protoc-gen-go-tableau-loader/messager.go index 360e328d..ce18b262 100644 --- a/cmd/protoc-gen-go-tableau-loader/messager.go +++ b/cmd/protoc-gen-go-tableau-loader/messager.go @@ -5,7 +5,7 @@ import ( "path/filepath" "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" - "github.com/tableauio/loader/internal/index/desc" + "github.com/tableauio/loader/internal/index" "github.com/tableauio/loader/internal/options" "github.com/tableauio/tableau/proto/tableaupb" "google.golang.org/protobuf/compiler/protogen" @@ -77,7 +77,7 @@ 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()) - indexDescriptor := desc.ParseIndexDescriptor(message.Desc) + indexDescriptor := index.ParseIndexDescriptor(message.Desc) // type definitions if options.NeedGenOrderedMap(message.Desc, options.LangGO) { diff --git a/internal/index/desc/desc.go b/internal/index/desc/desc.go deleted file mode 100644 index daf2b691..00000000 --- a/internal/index/desc/desc.go +++ /dev/null @@ -1,171 +0,0 @@ -package desc - -import ( - "fmt" - "strings" - - "github.com/tableauio/loader/internal/index" - "github.com/tableauio/tableau/proto/tableaupb" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/descriptorpb" -) - -type IndexDescriptor struct { - LevelMessage *LevelMessage // message hierarchy to the deepest level message which contains all index fields. -} - -type LevelField struct { - FD protoreflect.FieldDescriptor // index field descriptor - - // leveled fd list - // For example, if you have a message described as below and created an index on "PathUserID" - // fds are ["path", "user", "id"] - // - // message ItemConf { - // option (tableau.worksheet) = { - // name: "ItemConf" - // index: "PathUserID" - // }; - // map item_map = 1 [(tableau.field) = { key: "ID" layout: LAYOUT_VERTICAL }]; - // message Item { - // uint32 id = 1 [(tableau.field) = { name: "ID" }]; - // Path path = 2 [(tableau.field) = { name: "Path" }]; - // message Path { - // string dir = 1 [(tableau.field) = { name: "Dir" }]; - // User user = 2 [(tableau.field) = { name: "User" }]; - // message User { - // uint32 id = 1 [(tableau.field) = { name: "ID" }]; - // } - // } - // } - // } - LeveledFDList []protoreflect.FieldDescriptor -} - -// namespaced level info -type LevelMessage struct { - NextLevel *LevelMessage - - // Current level message's field which contains index fields. - // Only valid when NextLevel is not nil. - FD protoreflect.FieldDescriptor - - // Current level message's all index fields - Indexes []*LevelIndex -} - -type LevelIndex struct { - *index.Index - MD protoreflect.MessageDescriptor - ColFields []*LevelField - KeyFields []*LevelField -} - -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.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() { - parseRecursively(index, prefix+fieldOptName, fd.MapValue().Message(), levelMessage.NextLevel) - } else { - parseRecursively(index, prefix+fieldOptName, fd.Message(), levelMessage.NextLevel) - } - } -} - -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{ - FD: fd, - LeveledFDList: append(leveledFDList, fd), - } - levelFields = append(levelFields, field) - break - } else if fd.Kind() == protoreflect.MessageKind && - !fd.IsMap() && !fd.IsList() && - strings.HasPrefix(columnName, prefix+fieldOptName) { - levelFields = append(levelFields, - parseInSameLevel( - cols, prefix+fieldOptName, fd.Message(), - append(leveledFDList, fd), - )..., - ) - } - } - } - return levelFields -} - -func ParseIndexDescriptor(md protoreflect.MessageDescriptor) *IndexDescriptor { - descriptor := &IndexDescriptor{ - LevelMessage: parseLevelMessage(md), - } - indexes := index.ParseWSOptionIndex(md) - // parse indexes into level message - for _, index := range indexes { - if len(index.Cols) == 0 { - continue - } - parseRecursively(index, "", md, descriptor.LevelMessage) - } - // check duplicate index name - indexNameMap := map[string]*index.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 { - indexNameMap[name] = index.Index - } - } - } - return descriptor -} diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index 3934d6e5..808abeb3 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -5,21 +5,12 @@ import ( "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. } @@ -55,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{ @@ -116,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), )..., ) @@ -129,47 +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{} +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() - } else { - descriptor.MD = fd.Message() - } - 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) + parseRecursively(index, "", md, descriptor.LevelMessage) } // check duplicate index name - indexNameMap := map[string]int{} - for i, descriptor := range descriptors { - if j, ok := indexNameMap[descriptor.Name]; ok { - panic(fmt.Sprintf("duplicate index name on %v in %v: %v and %v", md.Name(), md.ParentFile().Path(), indexes[j], indexes[i])) - } else { - indexNameMap[descriptor.Name] = i + 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 { + indexNameMap[name] = index.Index + } } } - return descriptors + return descriptor } diff --git a/internal/index/desc/desc_test.go b/internal/index/descriptor_test.go similarity index 96% rename from internal/index/desc/desc_test.go rename to internal/index/descriptor_test.go index 4a5be062..365c83ea 100644 --- a/internal/index/desc/desc_test.go +++ b/internal/index/descriptor_test.go @@ -1,10 +1,9 @@ -package desc +package index import ( "testing" "github.com/stretchr/testify/assert" - "github.com/tableauio/loader/internal/index" "github.com/tableauio/loader/test/go-tableau-loader/protoconf" "google.golang.org/protobuf/reflect/protoreflect" ) @@ -29,7 +28,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { NextLevel: &LevelMessage{ Indexes: []*LevelIndex{ { - Index: &index.Index{ + Index: &Index{ Cols: []string{"Type"}, Name: "", }, @@ -44,7 +43,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, { - Index: &index.Index{ + Index: &Index{ Cols: []string{"Param"}, Keys: []string{"ID"}, Name: "ItemInfo", @@ -68,7 +67,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, { - Index: &index.Index{ + Index: &Index{ Cols: []string{"Default"}, Name: "ItemDefaultInfo", }, @@ -83,7 +82,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, { - Index: &index.Index{ + Index: &Index{ Cols: []string{"ExtType"}, Name: "ItemExtInfo", }, @@ -98,7 +97,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, { - Index: &index.Index{ + Index: &Index{ Cols: []string{"ID", "Name"}, Keys: []string{"Type", "UseEffectType"}, Name: "AwardItem", @@ -135,7 +134,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, { - Index: &index.Index{ + Index: &Index{ Cols: []string{"ID", "Type", "Param", "ExtType"}, Name: "SpecialItem", }, @@ -168,7 +167,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, { - Index: &index.Index{ + Index: &Index{ Cols: []string{"PathDir"}, Name: "ItemPathDir", }, @@ -184,7 +183,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, { - Index: &index.Index{ + Index: &Index{ Cols: []string{"PathName"}, Name: "ItemPathName", }, @@ -200,7 +199,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, { - Index: &index.Index{ + Index: &Index{ Cols: []string{"PathFriendID"}, Name: "ItemPathFriendID", }, @@ -217,7 +216,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, { - Index: &index.Index{ + Index: &Index{ Cols: []string{"UseEffectType"}, Name: "UseEffectType", }, @@ -250,7 +249,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { NextLevel: &LevelMessage{ Indexes: []*LevelIndex{ { - Index: &index.Index{ + Index: &Index{ Cols: []string{"Title"}, Name: "", }, @@ -282,7 +281,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { FD: (&protoconf.ActivityConf_Activity{}).ProtoReflect().Descriptor().Fields().ByName("chapter_map"), Indexes: []*LevelIndex{ { - Index: &index.Index{ + Index: &Index{ Cols: []string{"ActivityName"}, Name: "", }, @@ -301,7 +300,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { FD: (&protoconf.ActivityConf_Activity_Chapter{}).ProtoReflect().Descriptor().Fields().ByName("section_map"), Indexes: []*LevelIndex{ { - Index: &index.Index{ + Index: &Index{ Cols: []string{"ChapterID"}, Name: "", }, @@ -316,7 +315,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, { - Index: &index.Index{ + Index: &Index{ Cols: []string{"ChapterName"}, Keys: []string{"AwardID"}, Name: "NamedChapter", @@ -345,7 +344,7 @@ func Test_ParseIndexDescriptor(t *testing.T) { NextLevel: &LevelMessage{ Indexes: []*LevelIndex{ { - Index: &index.Index{ + Index: &Index{ Cols: []string{"SectionItemID"}, Name: "Award", },