autogenpb/protoParse.go

175 lines
4.4 KiB
Go

package main
// auto run protoc with the correct args
import (
"bufio"
"fmt"
"os"
"strings"
"go.wit.com/log"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
// this parses the .proto file and handles anything with `autogenpb: `
// 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)
prefix := "message " + base + "s {" // to conform, it must have an added 's'
if !strings.HasPrefix(line, prefix) {
// log.Info("nope", prefix, "line", line)
// nope, not this line
continue
}
// found the matching message
f.Bases = f.parseForMessage(line)
line = scanner.Text()
fields := strings.Fields(line)
if fields[0] == "string" && fields[1] != "uuid" {
return fmt.Errorf("proto file does not have a UUID")
}
// ok, uuid is here
f.Uuid = line
log.Info("found UUID:", line)
line = scanner.Text()
fields = strings.Fields(line)
if fields[0] == "string" && fields[1] != "version" {
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
}
return fmt.Errorf("proto file error %s", f.Filename)
}
func (pb *Files) protoParse(f *File) error {
// does the file conform to the standard? (also reads in UUID & Version)
if err := pb.hasPluralMessage(f); err != nil {
return err
}
log.Info(f.Filename, "is valid so far")
// read in the .proto file
data, err := os.ReadFile(f.Filename)
if err != nil {
return err
}
var curmsg *MsgName
// parse the proto file for message struct names
for _, line := range strings.Split(string(data), "\n") {
base := cases.Title(language.English, cases.NoLower).String(f.Filebase)
if strings.HasPrefix(line, "message ") {
curmsg = f.parseForMessage(line)
prefix := "message " + base + " {" // only look for this for now
if strings.HasPrefix(line, prefix) {
f.Base = curmsg
f.MsgNames = append(f.MsgNames, curmsg)
} else {
f.MsgNames = append(f.MsgNames, curmsg)
}
}
if strings.HasPrefix(line, "}") {
curmsg = nil
}
if curmsg == nil {
// can't contiue on nil below here
continue
}
parts := strings.Fields(line)
msgvar := parseMsgVar(line)
if msgvar == nil {
// log.Info("Junk in .proto file? line did not contain a message var:", line)
continue
}
if strings.Contains(line, "autogenpb:sort") {
newS := cases.Title(language.English, cases.NoLower).String(parts[1])
log.Info("Addded Sort:", newS, "in struct", curmsg.Name)
curmsg.Sort = append(curmsg.Sort, newS)
msgvar.HasSort = true
}
if strings.Contains(line, "autogenpb:unique") {
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)
msgvar.HasUnique = true
}
curmsg.Vars = append(curmsg.Vars, msgvar)
}
return nil
}
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]
v.VarName = parts[2]
return v
}
v.VarType = parts[0]
v.VarName = parts[1]
return v
}
// looks for mutex and marshal entries
func (f *File) parseForMessage(line string) *MsgName {
fields := strings.Fields(line)
if fields[0] != "message" {
return nil
}
msgName := cases.Title(language.English, cases.NoLower).String(fields[1])
log.Info("found messge:", msgName)
msg := new(MsgName)
msg.Name = msgName
msg.Lockname = f.Filebase + "Mu" // this should be lowercase. do not export the Mutex
if strings.Contains(line, "`autogenpb:mutex`") {
msg.DoMutex = true
log.Info("Added Mutex=true:", msg.Name)
}
if strings.Contains(line, "`autogenpb:marshal`") {
msg.DoMarshal = true
log.Info("Added Marshal=true:", msg.Name)
}
return msg
}