diff --git a/Makefile b/Makefile index 7f0b91d..049a547 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,27 @@ VERSION = $(shell git describe --tags) BUILDTIME = $(shell date +%Y.%m.%d_%H%M) -simple: build - make -C example clean simpleMutexGlobal goimports vet - make -C example clean simpleMutexProtoc goimports vet +simple: test + # make -C example clean simpleMutexGlobal goimports vet + # make -C example clean simpleMutexProtoc goimports vet # make -C example deleteproto full: install clean auto goimports vet build test install @echo everything worked and the example ran -test: goimports build test + +# if this passes, it should be OK to 'go install' +test: + reset + make goimports vet build + make -C example testGlobal + make -C example testProtoc + +dryrun: build + make -C example dryrun + +dryrun-clean: clean auto build + make -C example dryrun vet: @GO111MODULE=off go vet @@ -31,7 +43,7 @@ build: goimports bak: mv -f autogenpb autogenpb.last -install: +install: test GO111MODULE=off go install \ -ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}" @@ -40,12 +52,6 @@ auto: autogenpb --proto file.proto --package main # rm -f auto.sort.pb.go auto.newsort.pb.go # auto.marshal.pb.go -test: - make -C example rawproto - # The Go Protocol Buffers library embeds a sync.Mutex within the MessageState struct to prevent unintended shallow copies of message structs - # It only fails in Marshal() functions though. That is dumb. - make -C example modproto # THIS DOES NOT WORK. It could work however. This autogenerated code could be used to debug it. - junk: cd example; rm -f go.* *.pb.go cd example; ../autogenpb --proto file.proto --package yellow diff --git a/example/Makefile b/example/Makefile index 22a8c65..3a045bd 100644 --- a/example/Makefile +++ b/example/Makefile @@ -4,6 +4,20 @@ BUILDTIME = $(shell date +%Y.%m.%d_%H%M) all: clean simpleMutexProtoc goimports build ./example +# if this passes, then autogenpb should be working +# and it is OK to 'go install' the binary +test: testGlobal testProtoc + +testGlobal: + make clean + ../autogenpb --proto fruit.proto --package main --mutex=false # first build with a global mutex + make build + +testProtoc: + make clean + ../autogenpb --proto fruit.proto --package main # inserts mutex into protoc .pb.go file + make build + modproto: clean withMutex goimports vet build ./example @@ -47,6 +61,10 @@ withoutMutex: clean ../autogenpb --proto file.proto --package main --mutex=false ../autogenpb --proto patchset.proto --package main --mutex=false +dryrun: + ../autogenpb --proto fruit.proto --package main --dry-run + # ../autogenpb --proto file.proto --package main + goimports: goimports -w *.go diff --git a/example/file.proto b/example/file.proto index 18b096a..2f1b377 100644 --- a/example/file.proto +++ b/example/file.proto @@ -41,6 +41,7 @@ message MsgName { repeated string sort = 7; // keys to sort on repeated string unique = 8; // if the fields should have AppendUnique() functions repeated MsgVar vars = 9; // store all the vars in the message + bool needIter = 10; // true if the sort iterator has not been generated yet } message File { diff --git a/example/fruit.proto b/example/fruit.proto index 92e46ef..d43a741 100644 --- a/example/fruit.proto +++ b/example/fruit.proto @@ -22,17 +22,35 @@ message Apple { // `autogenpb:marshal` } message Pear { - string name = 1; // - string favorite = 2; // `autogenpb:sort` + string name = 1; // `autogenpb:sort` + string favorite = 2; // `autogenpb:sort` `autogenpb:unique` +} + +message Banana { + repeated string name = 1; // `autogenpb:sort` + string favorite = 2; // `autogenpb:sort` `autogenpb:unique` + string country = 3; // `autogenpb:sort` +} + +message Basket { + repeated string name = 1; // `autogenpb:sort` `autogenpb:unique` + string favorite = 2; // `autogenpb:sort` `autogenpb:unique` + int64 price = 3; // `autogenpb:sort` + repeated Banana banna = 4; + repeated Pear pears = 5; + repeated Apple stacks = 6; } // "Fruit" must exist. you can put anything in it message Fruit { // `autogenpb:marshal` - string brand = 1; // `autogenpb:unique` `autogenpb:sort` - repeated Apple apples = 2; - repeated Pear pears = 3; - string UPC = 4; // `autogenpb:sort` `autogenpb:unique` - string city = 5; // `autogenpb:sort` + string brand = 1; // `autogenpb:unique` `autogenpb:sort` + repeated Apple apples = 2; + repeated Pear pears = 3; + string UPC = 4; // `autogenpb:sort` `autogenpb:unique` + string city = 5; // `autogenpb:sort` + repeated Pear notpears = 6; + repeated Pear fakepears = 7; + repeated Basket gifts = 8; } // "Fruits" MUST EXIST and start exactly this way @@ -43,4 +61,5 @@ message Fruits { // `autogenpb:marshal` `autogenpb:mutex` repeated Fruit Fruits = 3; // THIS MUST BE "Fruit" and then "Fruit" + "s" // you can add additional things here but the three lines above must conform to the standard above int64 cost = 4; + map junk = 5; } diff --git a/file.proto b/file.proto index bb9f614..f2f6887 100644 --- a/file.proto +++ b/file.proto @@ -43,9 +43,27 @@ message MsgName { repeated string sort = 7; // keys to sort on repeated string unique = 8; // if the fields should have AppendUnique() functions repeated MsgVar vars = 9; // store all the vars in the message + bool needIter = 10; // true if the sort iterator has not been generated yet + bool needAll = 11; // true if the sort iterator has not been generated yet +} + +message Sort { + string msgName = 1; // `autogenpb:unique` File + string varType = 2; // `autogenpb:unique` MsgName + string varName = 3; // `autogenpb:unique` msgNames, sortNames + string lockname = 4; // + bool needAll = 5; // +} + +message Find { + string parent = 1; // `autogenpb:unique` File + string varType = 2; // `autogenpb:unique` MsgName + string varName = 3; // `autogenpb:unique` msgNames, sortNames + bool needAll = 4; // } message File { + // `autogenpb:var:w io.Writer` string Package = 1; // whatever the package name is at the top of the .go file string filename = 2; // yellow.proto string pbfilename = 3; // yellow.pb.go @@ -58,6 +76,8 @@ message File { // every struct in this proto file, this file has: "Apple", "Apples", ... "File", etc... repeated MsgName msgNames = 9; repeated MsgName sortNames = 10; // variables that are repeated can have the standard functions generated (Sort(), etc) + map iterMap = 11; + repeated Sort toSort = 12; // variables that are repeated can have the standard functions generated (Sort(), etc) } // I know, I know, the whole point of using protobuf diff --git a/generate.go b/generate.go index b22b3f7..418513f 100644 --- a/generate.go +++ b/generate.go @@ -15,19 +15,212 @@ import ( func (pb *Files) makeNewSortfile(pf *File) error { wSort, _ := os.OpenFile(pf.Filebase+".sort.pb.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) defer wSort.Close() - wFind, _ := os.OpenFile(pf.Filebase+".find.pb.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) - defer wFind.Close() header(wSort, pf) + pf.syncLock(wSort) - // pf.Base.iterTop(wSort) - // pf.Base.iterNext(wSort) - // pf.selectAllFunc(wSort) - // pf.iterSelect(wSort) + log.Printf("START ITERATORS\n") + // add iterators for all the structs to be used + for i, msg := range pf.allMsg() { + PARENT := msg.Name + LOCK := msg.Lockname - // pf.sortByFunc(wSort, pf.Bases, pf.Base) - pf.newGenerateSort(wSort, pf.Bases) - pf.newGenerateSort(wSort, pf.Base) + funcdef := newIter(wSort, msg) + log.Printf("ITER: %-2d %20s %20s %20s %20s %s\n", i, PARENT, "", "", LOCK, funcdef) + } + log.Printf("END ITERATORS\n") + log.Printf("\n") + + log.Printf("START COMPARE\n") + for i, msg := range pf.allMsg() { + PARENT := msg.Name + for _, v := range msg.Vars { + if !v.HasSort { + continue + } + VARNAME := v.VarName + funcdef := newSortType(wSort, PARENT, VARNAME) + log.Printf("TYPE: %-2d %20s %20s %20s %10s %s\n", i, PARENT, "", "", "", funcdef) + } + } + log.Printf("END COMPARE\n") + log.Printf("\n") + + // add selectAll() functions for all the sort combinations that will be used + for i, s := range pf.ToSort { + // log.Printf("SORT: %-2d %20s %20s %20s %20s\n", i, s.MsgName, s.VarType, s.VarName, s.Lockname) + FRUIT := s.MsgName + APPLE := s.VarType + APPLES := s.VarName + LOCK := s.Lockname + funcdef := newIterAll(wSort, FRUIT, APPLE, APPLES, LOCK) + log.Printf("JUNK: %-2d %20s %20s %20s %20s %s\n", i, FRUIT, APPLE, "", LOCK, funcdef) + } + log.Printf("\n") + + // make the sort iterators selectAll() + for i, s := range pf.ToSort { + PARENT := s.MsgName + CHILD := s.VarType + VARNAME := s.VarName + LOCK := s.Lockname + + funcdef := addSelectAll(wSort, PARENT, CHILD, VARNAME, LOCK) + log.Printf("SORT: %-2d %20s %20s %20s %20s %s\n", i, PARENT, CHILD, VARNAME, LOCK, funcdef) + } + log.Printf("\n") + + // make Len() + for _, msg := range pf.allMsg() { + PARENT := msg.Name + LOCK := msg.Lockname + + for i, v := range msg.Vars { + if v.IsRepeated { + CHILD := v.VarType + VARNAME := v.VarName + // funcdef := "func (x *" + PARENT + ") Len" + VARNAME + "() int " + CHILD + " name:" + VARNAME + + if PARENT == VARNAME { + // special case + funcdef := addLenFunc(wSort, PARENT, VARNAME, LOCK) // + " " + v.VarType + " name:" + v.VarName + funcdef += " # is special struct=varname" + log.Printf("LEN: %-2d %20s %20s %20s %20s %s\n", i, PARENT, CHILD, VARNAME, LOCK, funcdef) + } + } + } + } + + log.Printf("\n") + log.Printf(" %-2s %20s %20s %20s %20s\n", "", "PARENT STRUCT", "VAR STRUCT TYPE", "VAR NAME", "LOCK") + // for i, s := range slices.Backward(pf.ToSort) { + for i, s := range pf.ToSort { + var funcname string + PARENT := s.MsgName + CHILD := s.VarType + VARNAME := s.VarName + LOCK := s.Lockname + + log.Printf("SORT: %-2d %20s %20s %20s %20s %s\n", i, PARENT, CHILD, VARNAME, LOCK, "") + + var FUNCTYPE string + if PARENT == VARNAME { + FUNCTYPE = PARENT + } else { + FUNCTYPE = VARNAME + } + + if PARENT == VARNAME { + funcname := addAllFunc(wSort, PARENT, CHILD, VARNAME) + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "done", "", funcname) + } + if s.VarType+"s" == s.VarName { + funcname = "func (x *" + FUNCTYPE + ") All() *[]iter" + s.VarType + } else { + funcname = "func (x *" + FUNCTYPE + ") all" + s.VarName + "() *[]iter" + s.VarType + } + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + msg := pf.findMsg(s.VarType) + if msg == nil { + return fmt.Errorf("failed to find struct %s", s.VarType) + } + for _, v := range msg.Vars { + if v.HasSort { + funcname := "func (x *" + FUNCTYPE + ") SortBy" + v.VarName + "(" + v.VarType + ") *[]iter" + s.VarType + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + // funcdef := "func (x *"+FRUIT+") SortBy"+COLOR+"() *"+APPLE+"Iterator" + + if v.VarType == "string" { + funcdef := newSortBy(wSort, FUNCTYPE, CHILD, VARNAME, v.VarName) + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcdef) + } else { + funcname := "func (x *" + FUNCTYPE + ") SortBy" + v.VarName + "(" + v.VarType + ") *[]iter" + s.VarType + " # can not do this yet" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + } + } + + var ucount int + for _, v := range msg.Vars { + if v.HasUnique { + ucount += 1 + funcname := "func (x *" + FUNCTYPE + ") AppendUnique" + v.VarName + "(" + v.VarType + ")" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + } + for _, v := range msg.Vars { + if v.HasUnique { + funcname := "func (x *" + FUNCTYPE + ") DeleteBy" + v.VarName + "(" + v.VarType + ") bool" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + } + + for _, v := range msg.Vars { + if v.HasUnique { + funcname = "func (x *" + FUNCTYPE + ") FindBy" + v.VarName + "(a " + v.VarType + ") *" + s.VarType + "(using" + v.VarName + ")" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } else { + if v.VarType == "string" { + funcname = "func (x *" + FUNCTYPE + ") FindBy" + v.VarName + "(a string) []*" + s.VarType + " ???" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + } + } + if ucount == 1 { + for _, v := range msg.Vars { + if !v.HasUnique { + continue + } + funcname = "func (x *" + FUNCTYPE + ") Insert(a *" + v.VarType + ") (*" + CHILD + ", isNew bool)" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + } + if ucount > 1 { + funcname = "func (x *" + FUNCTYPE + ") Insert(a *" + CHILD + ") (*" + CHILD + ", isNew bool)" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + + } + + /* + // everything struct used in here needs a sort iterator (but just one) + for FRUIT, APPLE := range pf.IterMap { + msg := pf.findMsg(FRUIT) + if msg == nil { + return fmt.Errorf("failed to find struct %s", FRUIT) + } + log.Printf("Add newIter() message %s\n", FRUIT) + newIter(wSort, msg) + + child := pf.findMsg(APPLE) + if child == nil { + return fmt.Errorf("failed to find struct %s", APPLE) + } + log.Printf("Add newIter() message %s\n", APPLE) + newIter(wSort, child) + + // now add the allKEY() functions + msg.newIterAll(wSort, FRUIT, APPLE) + } + */ + + /* + log.Info("START") + log.Printf("Add all() FRUIT=%s APPLE=%s\n", FRUIT, APPLE) + addAllFunc(w, FRUIT, APPLE, LOCK) + log.Printf("Add len() FRUIT=%s APPLES=%s\n", FRUIT, APPLES) + addLenFunc(w, FRUIT, APPLES, LOCK) + log.Printf("Add selectAll() FRUIT=%s APPLE=%s APPLES=%s\n", FRUIT, APPLE, APPLES) + addSelectAll(w, FRUIT, APPLE, APPLES, LOCK) + // newIter(w, FRUIT, APPLE, APPLES, LOCK) + log.Info("END") + */ + + /* + return nil + pf.newGenerateSort(wSort, pf.Bases) + pf.newGenerateSort(wSort, pf.Base) + */ /* pf.appendUnique(wFind) // Append() enforce no unique keys @@ -39,44 +232,59 @@ func (pb *Files) makeNewSortfile(pf *File) error { pf.findFunc(wFind) */ - header(wFind, pf) - pf.syncLock(wFind) - pf.specialBases(wFind) + /* + wFind, _ := os.OpenFile(pf.Filebase+".find.pb.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + defer wFind.Close() + header(wFind, pf) + pf.specialBases(wFind) - // attempt to add sort functions for pf.Base - if err := pf.processMessage(pf.Bases, wSort, wFind); err != nil { - return err - } - if err := pf.processMessage(pf.Base, wSort, wFind); err != nil { - return err - } + // attempt to add sort functions for pf.Base + if err := pf.processMessage(pf.Bases, wSort, wFind); err != nil { + return err + } + if err := pf.processMessage(pf.Base, wSort, wFind); err != nil { + return err + } + */ return nil } +/* +func addIterNew(w io.Write, msg *MsgName { + var FRUIT string = parent.Name + var LOCK string = parent.Lockname + newIter(w, FRUIT, APPLE, APPLES, LOCK) +} +*/ + func (pf *File) newGenerateSort(w io.Writer, parent *MsgName) error { var FRUIT string = parent.Name var LOCK string = parent.Lockname - var allLen bool - for _, v := range parent.Vars { if !v.IsRepeated { continue } var APPLES string = v.VarName var APPLE string = v.VarType - if !allLen { - // only can run these once for now - addAllFunc(w, FRUIT, APPLE, LOCK) - addLenFunc(w, FRUIT, APPLES, LOCK) - allLen = true - } - addSelectAll(w, FRUIT, APPLE, APPLES, LOCK) - newIter(w, FRUIT, APPLE, APPLES, LOCK) msg := pf.findMsg(APPLE) if msg == nil { return fmt.Errorf("failed to find struct %s", APPLE) } + if msg.NeedIter { + parent.NeedIter = false + // only can run these once for now + log.Info("START") + log.Printf("Add all() FRUIT=%s APPLE=%s\n", FRUIT, APPLE) + // addAllFunc(w, FRUIT, APPLE, LOCK) + log.Printf("Add len() FRUIT=%s APPLES=%s\n", FRUIT, APPLES) + addLenFunc(w, FRUIT, APPLES, LOCK) + log.Printf("Add selectAll() FRUIT=%s APPLE=%s APPLES=%s\n", FRUIT, APPLE, APPLES) + addSelectAll(w, FRUIT, APPLE, APPLES, LOCK) + // newIter(w, FRUIT, APPLE, APPLES, LOCK) + log.Info("END") + } + for _, v := range msg.Vars { if !v.HasSort { continue @@ -104,6 +312,16 @@ func (pf *File) findMsg(s string) *MsgName { return nil } +func (pf *File) allMsg() []*MsgName { + var all []*MsgName + all = append(all, pf.Bases) + all = append(all, pf.Base) + for _, msg := range pf.MsgNames { + all = append(all, msg) + } + return all +} + func (pf *File) specialBases(wFind io.Writer) { var FRUIT string = cases.Title(language.English, cases.NoLower).String(pf.Bases.Name) var APPLES string = cases.Title(language.English, cases.NoLower).String(pf.Bases.Name) diff --git a/generateSort.go b/generateSort.go index a710212..fd2291d 100644 --- a/generateSort.go +++ b/generateSort.go @@ -5,16 +5,11 @@ import ( "io" ) -// only make one of these for each message in the protobuf file -func newIter(w io.Writer, FRUIT, APPLE, APPLES, LOCK string) { - fmt.Fprintln(w, "// DEFINE THE ITERATOR. is unique to the "+APPLE+" protobuf message") - fmt.Fprintln(w, "// itializes a new iterator.") - fmt.Fprintln(w, "func New"+APPLE+"Iterator(things []*"+APPLE+") *"+APPLE+"Iterator {") - fmt.Fprintln(w, " return &"+APPLE+"Iterator{things: things}") - fmt.Fprintln(w, "}") - fmt.Fprintln(w, "") +func newIterAll(w io.Writer, FRUIT, APPLE, APPLES, LOCK string) string { + funcdef := "func (x *" + FRUIT + ") all" + APPLES + "() []*" + APPLE + " {" + fmt.Fprintln(w, "// safely returns a slice of pointers to the FRUIT protobufs") - fmt.Fprintln(w, "func (x *"+FRUIT+") all"+APPLES+"() []*"+APPLE+" {") + fmt.Fprintln(w, funcdef) fmt.Fprintln(w, " "+LOCK+".RLock()") fmt.Fprintln(w, " defer "+LOCK+".RUnlock()") fmt.Fprintln(w, "") @@ -28,6 +23,26 @@ func newIter(w io.Writer, FRUIT, APPLE, APPLES, LOCK string) { fmt.Fprintln(w, " return tmp") fmt.Fprintln(w, "}") fmt.Fprintln(w, "") + + return funcdef +} + +// only make one of these for each message in the protobuf file +func newIter(w io.Writer, msg *MsgName) string { + if !msg.NeedIter { + return "iter already done for " + msg.Name + } + msg.NeedIter = false + APPLE := msg.Name + + funcdef := "func New" + APPLE + "Iterator(things []*" + APPLE + ") *" + APPLE + "Iterator" + + fmt.Fprintln(w, "// DEFINE THE", APPLE, "ITERATOR.") + fmt.Fprintln(w, "// itializes a new iterator.") + fmt.Fprintln(w, funcdef, "{") + fmt.Fprintln(w, " return &"+APPLE+"Iterator{things: things}") + fmt.Fprintln(w, "}") + fmt.Fprintln(w, "") fmt.Fprintln(w, "type "+APPLE+"Iterator struct {") fmt.Fprintln(w, " sync.RWMutex // this isn't getting used properly yet?") fmt.Fprintln(w, "") @@ -53,9 +68,28 @@ func newIter(w io.Writer, FRUIT, APPLE, APPLES, LOCK string) { fmt.Fprintln(w, "") fmt.Fprintln(w, "// END DEFINE THE ITERATOR") fmt.Fprintln(w, "") + + return funcdef } -func newSortBy(w io.Writer, FRUIT, APPLE, APPLES, COLOR string) { +// maybe there are better ways in GO now adays // that's fine though. this is easy to read +// TODO; figure out what types this actually works on +// TODO; add timestamppb compare +func newSortType(w io.Writer, STRUCT, VARNAME string) string { + fmt.Fprintln(w, "// sort struct by", VARNAME) + fmt.Fprintln(w, "type "+STRUCT+VARNAME+" []*"+STRUCT+"") + fmt.Fprintln(w, "") + fmt.Fprintln(w, "func (a "+STRUCT+VARNAME+") Len() int { return len(a) }") + fmt.Fprintln(w, "func (a "+STRUCT+VARNAME+") Less(i, j int) bool { return a[i]."+VARNAME+" < a[j]."+VARNAME+" }") + fmt.Fprintln(w, "func (a "+STRUCT+VARNAME+") Swap(i, j int) { a[i], a[j] = a[j], a[i] }") + fmt.Fprintln(w, "") + + return "type " + STRUCT + VARNAME + " []*" + STRUCT + " // { return a[i]." + VARNAME + " < a[j]." + VARNAME + " }" +} + +func newSortBy(w io.Writer, FRUIT, APPLE, APPLES, COLOR string) string { + funcdef := "func (x *" + FRUIT + ") SortBy" + COLOR + "() *" + APPLE + "Iterator // field: " + COLOR + fmt.Fprintln(w, "// START sort by ", COLOR, "(this is all you need once the Iterator is defined)") fmt.Fprintln(w, "type "+APPLE+COLOR+" []*"+APPLE+"") fmt.Fprintln(w, "") @@ -72,32 +106,42 @@ func newSortBy(w io.Writer, FRUIT, APPLE, APPLES, COLOR string) { fmt.Fprintln(w, " return iterator") fmt.Fprintln(w, "}") fmt.Fprintln(w, "// END sort by", COLOR) + + return funcdef } -func addAllFunc(w io.Writer, FRUIT, APPLE, LOCK string) { - fmt.Fprintln(w, "func (x *"+FRUIT+") All() *"+APPLE+"Iterator {") - fmt.Fprintln(w, " "+APPLE+"Pointers := x.selectAll"+APPLE+"()") +func addAllFunc(w io.Writer, FRUIT, APPLE, APPLES string) string { + funcdef := "func (x *" + FRUIT + ") All() *" + APPLE + "Iterator {" + + fmt.Fprintln(w, funcdef) + fmt.Fprintln(w, " "+APPLE+"Pointers := x.selectAll"+APPLES+"()") fmt.Fprintln(w, "") fmt.Fprintln(w, " iterator := New"+APPLE+"Iterator("+APPLE+"Pointers)") fmt.Fprintln(w, " return iterator") fmt.Fprintln(w, "}") fmt.Fprintln(w, "") + + return funcdef } -func addLenFunc(w io.Writer, FRUIT, APPLES, LOCK string) { +func addLenFunc(w io.Writer, FRUIT, APPLES, LOCK string) string { + funcdef := "func (x *" + FRUIT + ") Len() int {" fmt.Fprintln(w, "") - fmt.Fprintln(w, "func (x *"+FRUIT+") Len() int {") + fmt.Fprintln(w, funcdef) fmt.Fprintln(w, " "+LOCK+".RLock()") fmt.Fprintln(w, " defer "+LOCK+".RUnlock()") fmt.Fprintln(w, "") fmt.Fprintln(w, " return len(x."+APPLES+")") fmt.Fprintln(w, "}") fmt.Fprintln(w, "") + + return funcdef } -func addSelectAll(w io.Writer, FRUIT, APPLE, APPLES, LOCK string) { +func addSelectAll(w io.Writer, FRUIT, APPLE, APPLES, LOCK string) string { + funcdef := "func (x *" + FRUIT + ") selectAll" + APPLES + "() []*" + APPLE fmt.Fprintln(w, "// safely returns a slice of pointers to the "+APPLE+" protobufs") - fmt.Fprintln(w, "func (x *"+FRUIT+") selectAll"+APPLE+"() []*"+APPLE+" {") + fmt.Fprintln(w, funcdef, "{") fmt.Fprintln(w, " "+LOCK+".RLock()") fmt.Fprintln(w, " defer "+LOCK+".RUnlock()") fmt.Fprintln(w, "") @@ -110,4 +154,6 @@ func addSelectAll(w io.Writer, FRUIT, APPLE, APPLES, LOCK string) { fmt.Fprintln(w, "") fmt.Fprintln(w, " return tmp") fmt.Fprintln(w, "}") + + return funcdef } diff --git a/human.go b/human.go index 0aebb80..c735bf4 100644 --- a/human.go +++ b/human.go @@ -1,11 +1,18 @@ package main import ( + "fmt" + "go.wit.com/log" ) +// This was just going to be a function to print the results to stdout + +// instead, it's the core logic of the whole app +// --dry-run on the command line will just print what would be generated + // print the protobuf in human form -func (pf *File) printMsgTable() { +func (pf *File) printMsgTable() error { pf.Bases.printMsg() pf.Base.printMsg() @@ -13,6 +20,85 @@ func (pf *File) printMsgTable() { for _, msg := range pf.MsgNames { msg.printMsg() } + + log.Printf("\n") + log.Printf(" %-2s %20s %20s %20s %20s\n", "", "PARENT STRUCT", "VAR STRUCT TYPE", "VAR NAME", "LOCK") + // for i, s := range slices.Backward(pf.ToSort) { + for i, s := range pf.ToSort { + var funcname string + STRUCT := s.MsgName + CHILD := s.VarType + VARNAME := s.VarName + LOCK := s.Lockname + + log.Printf("SORT: %-2d %20s %20s %20s %20s %s\n", i, STRUCT, CHILD, VARNAME, LOCK, "") + + var FUNCTYPE string + if STRUCT == VARNAME { + FUNCTYPE = STRUCT + } else { + FUNCTYPE = VARNAME + } + + if s.VarType+"s" == s.VarName { + funcname = "func (x *" + FUNCTYPE + ") All() *[]iter" + s.VarType + } else { + funcname = "func (x *" + FUNCTYPE + ") all" + s.VarName + "() *[]iter" + s.VarType + } + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + msg := pf.findMsg(s.VarType) + if msg == nil { + return fmt.Errorf("failed to find struct %s", s.VarType) + } + for _, v := range msg.Vars { + if v.HasSort { + funcname := "func (x *" + FUNCTYPE + ") SortBy" + v.VarName + "(" + v.VarType + ") *[]iter" + s.VarType + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + } + + var ucount int + for _, v := range msg.Vars { + if v.HasUnique { + ucount += 1 + funcname := "func (x *" + FUNCTYPE + ") AppendUnique" + v.VarName + "(" + v.VarType + ")" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + } + for _, v := range msg.Vars { + if v.HasUnique { + funcname := "func (x *" + FUNCTYPE + ") DeleteBy" + v.VarName + "(" + v.VarType + ") bool" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + } + + for _, v := range msg.Vars { + if v.HasUnique { + funcname = "func (x *" + FUNCTYPE + ") FindBy" + v.VarName + "(a " + v.VarType + ") *" + s.VarType + "(using" + v.VarName + ")" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } else { + if v.VarType == "string" { + funcname = "func (x *" + FUNCTYPE + ") FindBy" + v.VarName + "(a string) []*" + s.VarType + " ???" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + } + } + if ucount == 1 { + for _, v := range msg.Vars { + if !v.HasUnique { + continue + } + funcname = "func (x *" + FUNCTYPE + ") Insert(a *" + v.VarType + ") (*" + CHILD + ", isNew bool)" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + } + if ucount > 1 { + funcname = "func (x *" + FUNCTYPE + ") Insert(a *" + CHILD + ") (*" + CHILD + ", isNew bool)" + log.Printf(" %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname) + } + + } + return nil } func (msg *MsgName) printMsg() { diff --git a/main.go b/main.go index 59d4ded..df6d00d 100644 --- a/main.go +++ b/main.go @@ -2,9 +2,10 @@ // +build go1.20 // protobuf the way I am using them, require GO 1.20. I think. I could be wrong. +// The Go Protocol Buffers library embeds a sync.Mutex within the MessageState struct to prevent unintended shallow copies of message structs +// this optionally (but it is the default) inserts a mutex into the struct generated by protoc -// go:generate go-mod-clean -// go:generate autogenpb auto.proto +// go:generate autogenpb --proto file.proto package main @@ -25,6 +26,8 @@ import ( var VERSION string var BUILDTIME string +var fsort *os.File // the sort.pb.go output file + func main() { pp := arg.MustParse(&argv) @@ -51,6 +54,7 @@ func main() { pf := new(File) pb.Files = append(pb.Files, pf) pf.Filename = argv.Proto + pf.IterMap = make(map[string]string) pf.Filebase = strings.TrimSuffix(argv.Proto, ".proto") @@ -69,6 +73,21 @@ func main() { badExit(fmt.Errorf("Base was nil. 'message %s {` did not exist", pf.Filebase)) } + /* + // prep the output file + if !argv.DryRun { + var err error + fsort, err = os.OpenFile(pf.Filebase+".newsort.pb.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + badExit(err) + } + defer fsort.Close() + + header(fsort, pf) + pf.syncLock(fsort) + } + */ + // show the protobuf of the protobuf. It's like Inception pf.printMsgTable() diff --git a/protoParse.go b/protoParse.go index 8031785..2cefc80 100644 --- a/protoParse.go +++ b/protoParse.go @@ -56,15 +56,15 @@ func (pb *Files) hasPluralMessage(f *File) error { return fmt.Errorf("proto file error %s", f.Filename) } -func (pb *Files) protoParse(f *File) error { +func (pb *Files) protoParse(pf *File) error { // does the file conform to the standard? (also reads in UUID & Version) - if err := pb.hasPluralMessage(f); err != nil { + if err := pb.hasPluralMessage(pf); err != nil { return err } - log.Info(f.Filename, "is valid so far") + log.Info(pf.Filename, "is valid so far") // read in the .proto file - data, err := os.ReadFile(f.Filename) + data, err := os.ReadFile(pf.Filename) if err != nil { return err } @@ -74,7 +74,7 @@ func (pb *Files) protoParse(f *File) error { // parse the proto file for message struct names for _, line := range strings.Split(string(data), "\n") { if strings.HasPrefix(line, "message ") { - curmsg = f.parseForMessage(line) + curmsg = pf.parseForMessage(line) } // this logic isn't right. find end of message with more bravado if strings.HasPrefix(line, "}") { @@ -93,6 +93,10 @@ func (pb *Files) protoParse(f *File) error { // log.Info("Junk in .proto file? line did not contain a message var:", line) continue } + if msgvar.IsRepeated { + log.Info("ADDING ITER MAP", curmsg.Name, msgvar.VarType) + pf.IterMap[curmsg.Name] = msgvar.VarType + } if strings.Contains(line, "autogenpb:sort") { newS := cases.Title(language.English, cases.NoLower).String(parts[1]) @@ -110,9 +114,40 @@ func (pb *Files) protoParse(f *File) error { } curmsg.Vars = append(curmsg.Vars, msgvar) } + pf.makeSortTable() + + // for i, s := range slices.Backward(pf.ToSort) { return nil } +func (pf *File) makeSortTable() { + pf.sortWhat(pf.Bases) + pf.sortWhat(pf.Base) + + // everything else + for _, msg := range pf.MsgNames { + pf.sortWhat(msg) + } +} + +func (pf *File) sortWhat(msg *MsgName) { + for _, v := range msg.Vars { + if !v.IsRepeated { + continue + } + if check := pf.findMsg(v.VarType); check == nil { + // the VarType must be a struct + continue + } + s := new(Sort) + s.MsgName = msg.Name + s.VarType = v.VarType + s.VarName = v.VarName + s.Lockname = msg.Lockname + pf.ToSort = append(pf.ToSort, s) + } +} + func parseMsgVar(line string) *MsgVar { if strings.Contains(line, "//") { parts := strings.Split(line, "//") @@ -169,6 +204,7 @@ func (pf *File) parseForMessage(line string) *MsgName { log.Info("found messge:", msgName) msg.Name = msgName msg.Lockname = pf.Filebase + "Mu" // this should be lowercase. do not export the Mutex + msg.NeedIter = true if strings.Contains(line, "`autogenpb:mutex`") { msg.DoMutex = true