2024-11-30 15:06:57 -06:00
|
|
|
package main
|
|
|
|
|
|
|
|
// auto run protoc with the correct args
|
|
|
|
|
|
|
|
import (
|
2025-01-09 15:03:05 -06:00
|
|
|
"bufio"
|
|
|
|
"fmt"
|
2024-11-30 15:06:57 -06:00
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"go.wit.com/log"
|
|
|
|
"golang.org/x/text/cases"
|
|
|
|
"golang.org/x/text/language"
|
2025-01-12 06:28:12 -06:00
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2024-11-30 15:06:57 -06:00
|
|
|
)
|
|
|
|
|
2025-01-08 20:30:33 -06:00
|
|
|
// this parses the .proto file and handles anything with `autogenpb: `
|
|
|
|
|
2025-01-09 15:03:05 -06:00
|
|
|
// does the fruit.proto file have "message Fruits"
|
|
|
|
func (pb *Files) hasPluralMessage(f *File) error {
|
|
|
|
file, err := os.Open(f.Filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := scanner.Text()
|
|
|
|
|
|
|
|
base := cases.Title(language.English, cases.NoLower).String(f.Filebase)
|
2025-01-09 15:29:27 -06:00
|
|
|
prefix := "message " + base + "s {" // to conform, it must have an added 's'
|
2025-01-09 15:03:05 -06:00
|
|
|
if !strings.HasPrefix(line, prefix) {
|
|
|
|
// log.Info("nope", prefix, "line", line)
|
|
|
|
// nope, not this line
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2025-01-12 06:28:12 -06:00
|
|
|
scanner.Scan()
|
2025-01-09 15:03:05 -06:00
|
|
|
line = scanner.Text()
|
|
|
|
fields := strings.Fields(line)
|
2025-01-12 06:28:12 -06:00
|
|
|
// log.Info("GOT LINE", line)
|
2025-01-09 15:03:05 -06:00
|
|
|
if fields[0] == "string" && fields[1] != "uuid" {
|
2025-01-12 06:13:42 -06:00
|
|
|
f.noUuid()
|
2025-01-09 15:03:05 -06:00
|
|
|
return fmt.Errorf("proto file does not have a UUID")
|
|
|
|
}
|
|
|
|
// ok, uuid is here
|
|
|
|
f.Uuid = line
|
|
|
|
log.Info("found UUID:", line)
|
|
|
|
|
2025-01-12 06:28:12 -06:00
|
|
|
scanner.Scan()
|
2025-01-09 15:03:05 -06:00
|
|
|
line = scanner.Text()
|
|
|
|
fields = strings.Fields(line)
|
2025-01-12 06:28:12 -06:00
|
|
|
// log.Info("GOT LINE", line)
|
2025-01-09 15:03:05 -06:00
|
|
|
if fields[0] == "string" && fields[1] != "version" {
|
2025-01-12 06:13:42 -06:00
|
|
|
f.noUuid()
|
2025-01-09 15:03:05 -06:00
|
|
|
return fmt.Errorf("proto file does not have a version")
|
|
|
|
}
|
|
|
|
// found "version", the .proto file conforms
|
|
|
|
f.Version = line
|
|
|
|
log.Info("found Version:", line)
|
|
|
|
return nil
|
|
|
|
}
|
2025-01-12 06:13:42 -06:00
|
|
|
f.noPluralMessage()
|
2025-01-09 15:03:05 -06:00
|
|
|
return fmt.Errorf("proto file error %s", f.Filename)
|
|
|
|
}
|
|
|
|
|
2025-01-12 06:13:42 -06:00
|
|
|
func (pf *File) noPluralMessage() {
|
|
|
|
base := cases.Title(language.English, cases.NoLower).String(pf.Filebase)
|
|
|
|
|
|
|
|
log.Info("")
|
|
|
|
log.Info("###########################################################################")
|
|
|
|
log.Info("Your proto file", pf.Filename, "does not contain the correct 'message' definitions")
|
|
|
|
log.Info("")
|
|
|
|
log.Info("For autogenpb to work on your file", pf.Filename, ", you must have both names exactly:")
|
|
|
|
log.Info("")
|
|
|
|
log.Printf("message %s {\n", base)
|
|
|
|
log.Info("}")
|
|
|
|
log.Printf("message %s {\n", base+"s")
|
|
|
|
log.Info("}")
|
|
|
|
log.Info("")
|
|
|
|
log.Info("###########################################################################")
|
|
|
|
badExit(fmt.Errorf("proto file error %s", pf.Filename))
|
|
|
|
}
|
|
|
|
|
|
|
|
// message Fruits { // `autogenpb:marshal` `autogenpb:mutex`
|
|
|
|
// string uuid = 1; // `autogenpb:uuid:be926ad9-f07f-484c-adf2-d96eeabf3079`
|
|
|
|
// string version = 2; // `autogenpb:version:v0.0.1`
|
|
|
|
// repeated Fruit Fruits = 3; // THIS MUST BE "Fruit" and then "Fruit" + "s"
|
|
|
|
// }
|
|
|
|
|
|
|
|
func (pf *File) noUuid() {
|
|
|
|
base := cases.Title(language.English, cases.NoLower).String(pf.Filebase)
|
2025-01-12 06:28:12 -06:00
|
|
|
id := uuid.New()
|
2025-01-12 06:13:42 -06:00
|
|
|
|
|
|
|
log.Info("")
|
|
|
|
log.Info("###########################################################################")
|
|
|
|
log.Info("Your proto file", pf.Filename, "is incorrect for 'message' ", base+"s")
|
|
|
|
log.Info("")
|
|
|
|
log.Info("For autogenpb to work on your file", pf.Filename, ",", base+"s must be exactly:")
|
|
|
|
log.Info("")
|
2025-01-12 06:28:12 -06:00
|
|
|
log.Info("message", base+"s", "{ // `autogenpb:marshal` `autogenpb:mutex`")
|
|
|
|
log.Info(" string uuid = 1; // `autogenpb:uuid:" + id.String() + "`")
|
2025-01-12 06:13:42 -06:00
|
|
|
log.Info(" string version = 2; // `autogenpb:version:v0.0.1`")
|
2025-01-12 06:28:12 -06:00
|
|
|
log.Info(" repeated", base, base+"s", " = 3; // THIS MUST BE ", base, " and then ", base+"s")
|
2025-01-12 06:13:42 -06:00
|
|
|
log.Info("}")
|
|
|
|
log.Info("")
|
|
|
|
log.Info("If you don't have a UUID, you can use the randomly generated one here")
|
|
|
|
log.Info("")
|
|
|
|
log.Info("###########################################################################")
|
|
|
|
badExit(fmt.Errorf("proto file error %s", pf.Filename))
|
|
|
|
}
|
|
|
|
|
2025-01-12 01:32:52 -06:00
|
|
|
func (pb *Files) protoParse(pf *File) error {
|
2025-01-09 15:03:05 -06:00
|
|
|
// does the file conform to the standard? (also reads in UUID & Version)
|
2025-01-12 01:32:52 -06:00
|
|
|
if err := pb.hasPluralMessage(pf); err != nil {
|
2025-01-09 15:03:05 -06:00
|
|
|
return err
|
|
|
|
}
|
2025-01-12 01:32:52 -06:00
|
|
|
log.Info(pf.Filename, "is valid so far")
|
2025-01-09 15:03:05 -06:00
|
|
|
|
2024-11-30 15:06:57 -06:00
|
|
|
// read in the .proto file
|
2025-01-12 01:32:52 -06:00
|
|
|
data, err := os.ReadFile(pf.Filename)
|
2024-11-30 15:06:57 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2025-01-08 20:58:29 -06:00
|
|
|
var curmsg *MsgName
|
|
|
|
|
|
|
|
// parse the proto file for message struct names
|
2025-01-08 20:52:47 -06:00
|
|
|
for _, line := range strings.Split(string(data), "\n") {
|
|
|
|
if strings.HasPrefix(line, "message ") {
|
2025-01-12 01:32:52 -06:00
|
|
|
curmsg = pf.parseForMessage(line)
|
2025-01-08 20:58:29 -06:00
|
|
|
}
|
2025-01-10 19:37:32 -06:00
|
|
|
// this logic isn't right. find end of message with more bravado
|
2025-01-08 20:58:29 -06:00
|
|
|
if strings.HasPrefix(line, "}") {
|
|
|
|
curmsg = nil
|
2025-01-08 20:52:47 -06:00
|
|
|
}
|
2025-01-09 15:03:05 -06:00
|
|
|
if curmsg == nil {
|
2025-01-10 19:37:32 -06:00
|
|
|
// log.Info("curmsg == nil", line)
|
2025-01-09 15:03:05 -06:00
|
|
|
// can't contiue on nil below here
|
|
|
|
continue
|
|
|
|
}
|
2025-01-10 19:37:32 -06:00
|
|
|
// log.Info("curmsg != nil", line)
|
2025-01-08 20:52:47 -06:00
|
|
|
|
2024-11-30 15:06:57 -06:00
|
|
|
parts := strings.Fields(line)
|
2025-01-10 11:22:08 -06:00
|
|
|
msgvar := parseMsgVar(line)
|
|
|
|
if msgvar == nil {
|
|
|
|
// log.Info("Junk in .proto file? line did not contain a message var:", line)
|
|
|
|
continue
|
|
|
|
}
|
2025-01-12 01:32:52 -06:00
|
|
|
if msgvar.IsRepeated {
|
|
|
|
log.Info("ADDING ITER MAP", curmsg.Name, msgvar.VarType)
|
|
|
|
pf.IterMap[curmsg.Name] = msgvar.VarType
|
|
|
|
}
|
2025-01-09 03:42:29 -06:00
|
|
|
|
|
|
|
if strings.Contains(line, "autogenpb:sort") {
|
2025-01-09 17:15:53 -06:00
|
|
|
newS := cases.Title(language.English, cases.NoLower).String(parts[1])
|
2025-01-09 15:03:05 -06:00
|
|
|
log.Info("Addded Sort:", newS, "in struct", curmsg.Name)
|
|
|
|
curmsg.Sort = append(curmsg.Sort, newS)
|
2025-01-10 11:22:08 -06:00
|
|
|
msgvar.HasSort = true
|
2024-11-30 15:06:57 -06:00
|
|
|
}
|
2025-01-09 04:22:11 -06:00
|
|
|
|
2024-11-30 15:06:57 -06:00
|
|
|
if strings.Contains(line, "autogenpb:unique") {
|
2025-01-09 15:03:05 -06:00
|
|
|
newU := parts[1]
|
|
|
|
newU = cases.Title(language.English, cases.NoLower).String(newU)
|
|
|
|
log.Info("Added Unique:", newU, "in struct", curmsg.Name)
|
|
|
|
curmsg.Unique = append(curmsg.Unique, newU)
|
2025-01-10 11:22:08 -06:00
|
|
|
msgvar.HasUnique = true
|
2024-11-30 15:06:57 -06:00
|
|
|
}
|
2025-01-10 11:22:08 -06:00
|
|
|
curmsg.Vars = append(curmsg.Vars, msgvar)
|
2024-11-30 15:06:57 -06:00
|
|
|
}
|
2025-01-12 01:32:52 -06:00
|
|
|
pf.makeSortTable()
|
|
|
|
|
|
|
|
// for i, s := range slices.Backward(pf.ToSort) {
|
2024-11-30 15:06:57 -06:00
|
|
|
return nil
|
|
|
|
}
|
2024-12-01 22:21:09 -06:00
|
|
|
|
2025-01-12 01:32:52 -06:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-10 11:22:08 -06:00
|
|
|
func parseMsgVar(line string) *MsgVar {
|
|
|
|
if strings.Contains(line, "//") {
|
|
|
|
parts := strings.Split(line, "//")
|
|
|
|
if len(parts) == 0 {
|
|
|
|
// log.Info("parseMsgVar() nothing line", line)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
line = parts[0]
|
|
|
|
}
|
|
|
|
parts := strings.Fields(line)
|
|
|
|
if len(parts) < 3 {
|
|
|
|
// log.Info("parseMsgVar() len < 3", parts)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if parts[0] == "message" {
|
|
|
|
// this is the struct
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
v := new(MsgVar)
|
|
|
|
if parts[0] == "repeated" {
|
|
|
|
v.IsRepeated = true
|
|
|
|
v.VarType = parts[1]
|
2025-01-11 10:19:37 -06:00
|
|
|
v.VarName = cases.Title(language.English, cases.NoLower).String(parts[2])
|
2025-01-10 11:22:08 -06:00
|
|
|
return v
|
|
|
|
}
|
|
|
|
v.VarType = parts[0]
|
2025-01-11 10:19:37 -06:00
|
|
|
v.VarName = cases.Title(language.English, cases.NoLower).String(parts[1])
|
2025-01-10 11:22:08 -06:00
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2025-01-08 20:52:47 -06:00
|
|
|
// looks for mutex and marshal entries
|
2025-01-10 19:37:32 -06:00
|
|
|
func (pf *File) parseForMessage(line string) *MsgName {
|
2025-01-08 20:52:47 -06:00
|
|
|
fields := strings.Fields(line)
|
2025-01-10 19:37:32 -06:00
|
|
|
if len(fields) == 0 || fields[0] != "message" {
|
2025-01-08 20:58:29 -06:00
|
|
|
return nil
|
2025-01-08 20:52:47 -06:00
|
|
|
}
|
2025-01-10 19:37:32 -06:00
|
|
|
var msg *MsgName
|
|
|
|
msg = new(MsgName)
|
|
|
|
|
|
|
|
base := cases.Title(language.English, cases.NoLower).String(pf.Filebase)
|
|
|
|
prefix := "message " + base + "s {" // only look for this for now
|
|
|
|
if strings.HasPrefix(line, prefix) {
|
|
|
|
pf.Bases = msg
|
|
|
|
} else {
|
|
|
|
prefix := "message " + base + " {" // only look for this for now
|
|
|
|
if strings.HasPrefix(line, prefix) {
|
|
|
|
pf.Base = msg
|
|
|
|
} else {
|
|
|
|
pf.MsgNames = append(pf.MsgNames, msg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-09 15:29:27 -06:00
|
|
|
msgName := cases.Title(language.English, cases.NoLower).String(fields[1])
|
2025-01-09 15:03:05 -06:00
|
|
|
log.Info("found messge:", msgName)
|
2025-01-08 20:52:47 -06:00
|
|
|
msg.Name = msgName
|
2025-01-10 19:37:32 -06:00
|
|
|
msg.Lockname = pf.Filebase + "Mu" // this should be lowercase. do not export the Mutex
|
2025-01-12 01:32:52 -06:00
|
|
|
msg.NeedIter = true
|
2025-01-08 20:52:47 -06:00
|
|
|
|
|
|
|
if strings.Contains(line, "`autogenpb:mutex`") {
|
|
|
|
msg.DoMutex = true
|
2025-01-09 04:03:30 -06:00
|
|
|
log.Info("Added Mutex=true:", msg.Name)
|
2025-01-08 20:52:47 -06:00
|
|
|
}
|
|
|
|
if strings.Contains(line, "`autogenpb:marshal`") {
|
|
|
|
msg.DoMarshal = true
|
2025-01-09 04:03:30 -06:00
|
|
|
log.Info("Added Marshal=true:", msg.Name)
|
2025-01-08 20:52:47 -06:00
|
|
|
}
|
2025-01-08 20:58:29 -06:00
|
|
|
return msg
|
2025-01-08 20:52:47 -06:00
|
|
|
}
|