auto formatting is working well enough

This commit is contained in:
Jeff Carr 2025-02-01 07:32:00 -06:00
parent cfedee740c
commit 9a0e4a6d62
4 changed files with 182 additions and 57 deletions

View File

@ -16,61 +16,63 @@ package main;
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
message Apple { message Apple {
string name = 1; // `autogenpb:unique` // generates SortByxxx() and AppendUnique() functions string name = 1; // `autogenpb:unique` // generates SortByxxx() and AppendUnique() functions
string genus = 2; // `autogenpb:unique` // generates same thing here but SortByGenus() string genus = 2; // `autogenpb:unique` // generates same thing here but SortByGenus()
google.protobuf.Timestamp ctime = 3; // when the apple was born google.protobuf.Timestamp ctime = 3; // when the apple was born
} }
message Apples { message Apples {
string name = 1; // `autogenpb:unique` // generates SortByxxx() and AppendUnique() functions string name = 1; // `autogenpb:unique` // generates SortByxxx() and AppendUnique() functions
string genus = 2; // `autogenpb:unique` // generates same thing here but SortByGenus() string genus = 2; // `autogenpb:unique` // generates same thing here but SortByGenus()
repeated Apple apples = 3; repeated Apple apples = 3;
} }
message Pear { // `autogenpb:nomutex` message Pear { // `autogenpb:nomutex`
string name = 1; // `autogenpb:sort` string name = 1; // `autogenpb:sort`
string favorite = 2; // `autogenpb:sort` `autogenpb:unique` string favorite = 2; // `autogenpb:sort` `autogenpb:unique`
} }
message Pears { // `autogenpb:nomutex` message Pears { // `autogenpb:nomutex`
string name = 1; // `autogenpb:sort` string name = 1; // `autogenpb:sort`
string favorite = 2; // `autogenpb:sort` `autogenpb:unique` string favorite = 2; // `autogenpb:sort` `autogenpb:unique`
repeated Pear pears = 3; repeated Pear pears = 3;
} }
message Banana { // `autogenpb:nomutex` message Banana { // `autogenpb:nomutex`
repeated string name = 1; // `autogenpb:sort` repeated string name = 1; // `autogenpb:sort`
string favorite = 2; // `autogenpb:sort` `autogenpb:unique` string favorite = 2; // `autogenpb:sort` `autogenpb:unique`
string country = 3; // `autogenpb:sort` string country = 3; // `autogenpb:sort`
} }
message Basket { // `autogenpb:nomutex` message Basket { // `autogenpb:nomutex`
repeated string name = 1; // `autogenpb:sort` `autogenpb:unique` repeated string name = 1; // `autogenpb:sort` `autogenpb:unique`
string favorite = 2; // `autogenpb:sort` `autogenpb:unique` string favorite = 2; // `autogenpb:sort` `autogenpb:unique`
int64 price = 3; // `autogenpb:sort` int64 price = 3; // `autogenpb:sort`
repeated Banana banna = 4; // here for testing padding
repeated Pear pears = 5;
repeated Apple stacks = 6; repeated Banana banna = 4;
repeated Pear pears = 5;
repeated Apple stacks = 6;
} }
// "Fruit" must exist. you can put anything in it // "Fruit" must exist. you can put anything in it
message Fruit { message Fruit {
string brand = 1; // `autogenpb:unique` `autogenpb:sort` string brand = 1; // `autogenpb:unique` `autogenpb:sort`
Apple apples = 2; Apple apples = 2;
repeated Pear pears = 3; repeated Pear pears = 3;
string UPC = 4; // `autogenpb:sort` `autogenpb:unique` string UPC = 4; // `autogenpb:sort` `autogenpb:unique`
string city = 5; // `autogenpb:sort` string city = 5; // `autogenpb:sort`
Pears notpears = 6; Pears notpears = 6;
Pears fakepears = 7; Pears fakepears = 7;
repeated Basket gifts = 8; repeated Basket gifts = 8;
} }
// "Fruits" MUST EXIST and start exactly this way // "Fruits" MUST EXIST and start exactly this way
// It must be "Fruit" + 's' and must match the name of this file: "fruit.proto" // It must be "Fruit" + 's' and must match the name of this file: "fruit.proto"
message Fruits { // `autogenpb:marshal` `autogenpb:mutex` message Fruits { // `autogenpb:marshal` `autogenpb:mutex`
string uuid = 1; // `autogenpb:uuid:be926ad9-f07f-484c-adf2-d96eeabf3079` string uuid = 1; // `autogenpb:uuid:be926ad9-f07f-484c-adf2-d96eeabf3079`
string version = 2; // `autogenpb:version:v0.0.1` string version = 2; // `autogenpb:version:v0.0.1`
repeated Fruit Fruits = 3; // THIS MUST BE "Fruit" and then "Fruit" + "s" repeated Fruit Fruits = 3; // THIS MUST BE "Fruit" and then "Fruit" + "s"
int64 cost = 4; // you can add additional things here but the three lines above must conform to the standard above int64 cost = 4; // you can add additional things here but the three lines above must conform to the standard above
map<string, string> junk = 5; map<string, string> junk = 5;
} }

View File

@ -45,12 +45,14 @@ message Banana { // `autogenpb:nomutex`
} }
message Basket { // `autogenpb:nomutex` message Basket { // `autogenpb:nomutex`
repeated string name = 1; // `autogenpb:sort` `autogenpb:unique` repeated string name = 1; // `autogenpb:sort` `autogenpb:unique`
string favorite = 2; // `autogenpb:sort` `autogenpb:unique` string favorite = 2; // `autogenpb:sort` `autogenpb:unique`
int64 price = 3; // `autogenpb:sort` int64 price = 3; // `autogenpb:sort`
repeated Banana banna = 4; //
repeated Pear pears = 5;
repeated Apple stacks = 6; repeated Banana banna = 4;
repeated Pear pears = 5;
repeated Apple stacks = 6;
} }
// "Fruit" must exist. you can put anything in it // "Fruit" must exist. you can put anything in it

103
file.proto.new Normal file
View File

@ -0,0 +1,103 @@
syntax = "proto3";
// Look at "example/fruit.proto" not this file
// this file is actually used by autogenpb
// here are some docs, but probably it's just easier to run
// autogenpb on this file and see what gets autogenerated
// in this directory. All autogenerated files are named *.pb.go
// the 'uuid' standard at the end is an experiment
// establish a way to identify arbitrary .pb files
// You can generate Marshal & Unmarshal for any struct (message) you want
// You can generate SortBy and Append functions ONLY FOR 'repeated <message>'
// Also, those structs must be defined in the same file
// Additionally, you must use `autogenpb:mutex` on the parent struct.
// The autogenerated code requires a RW mutex and autogenpb will insert it into the struct
package main;
//
// below are the actual structs autogen uses
// autogen parses the .proto file and then store the information
// it needs in these protobuf files, then it processes the
// protobuf files to write out *.sort.pb.go and *.marshal.pb.go files
//
message MsgVar {
string varName = 1; // the variable name
string varType = 2; // the variable type
bool isRepeated = 3; // does the variable repeate
bool hasSort = 4; // marked with sort
bool hasUnique = 5; // marked with unique
}
message MsgName {
string name = 1; // the name of the message aka struct. for this example: "Shelf"
string lockname = 2; // name of the lockfile. ends in Mu
bool doMarshal = 3; // if msg struct should have Marshal & Unmarshal functions
bool doMutex = 4; // true if a mutex is needed for the message struct
bool doProtocMutex = 5; // an experiment to insert a mutex into the protoc generated msg struct (bad idea?)
bool mutexFound = 6; // true if the mutex was added to the protoc pb.go file
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
bool noMutex = 12; // only use the global mutex
}
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
string filebase = 4; // yellow
string uuid = 5; // the uuid to use in a func NewMsgName()
string version = 6; // the version to use in a func NewMsgName()
MsgName bases = 7; // the message in "plural" form
MsgName base = 8; // the primary repeated message for the master struct
// 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<string, string> iterMap = 11;
repeated Sort toSort = 12; // variables that are repeated can have the standard functions generated (Sort(), etc)
string goPath = 13; // the version to use in a func NewMsgName()
}
// I know, I know, the whole point of using protobuf
// is so you don't need a uuid or versions because it's
// inherently forward compatable. nonetheless, a simple stubbed out
// trivial and empty protobuf message can marshal and identify all the files
// also, this could be used to modify /usr/bin/file /usr/share/magic to identify the files
// maybe this is already been done and is pointless, but it seems like a good idea
message Files { // `autogenpb:marshal`
string uuid = 1; // `autogenpb:uuid:6c9ae4dd-648d-4b51-9738-bd59fb8fafd5`
string version = 2; // `autogenpb:version:v0.0.38`
repeated File Files = 3; // an array of each .proto file in the working directory
}
// this generic message is used by autogen to identify and
// then dump the uuid and version from any arbitrary .pb file
message Identify { // `autogenpb:marshal`
string uuid = 1; //
string version = 2; //
}

View File

@ -21,15 +21,9 @@ func protoReformat(filename string) error {
return err return err
} }
pf, err := os.OpenFile(filename+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
log.Info("file open error. permissions?", filename, err)
return err
}
defer pf.Close()
var inMessage bool var inMessage bool
var curmsg []string var curmsg []string
var newfile string
// gets the max vartype and varname // gets the max vartype and varname
for _, line := range strings.Split(string(data), "\n") { for _, line := range strings.Split(string(data), "\n") {
@ -59,6 +53,7 @@ func protoReformat(filename string) error {
inMessage = true inMessage = true
parts := strings.Fields(line) parts := strings.Fields(line)
if len(parts) > 3 { if len(parts) > 3 {
// hack to actually indent comments on the message line itself. you're welcome
start := parts[0] + " " + parts[1] + " " + parts[2] start := parts[0] + " " + parts[1] + " " + parts[2]
end := strings.Join(parts[3:], " ") end := strings.Join(parts[3:], " ")
offset := maxVarname + maxVartype + 16 - len(start) offset := maxVarname + maxVartype + 16 - len(start)
@ -66,29 +61,39 @@ func protoReformat(filename string) error {
hmm := "%s %" + pad + "s %s" hmm := "%s %" + pad + "s %s"
line = fmt.Sprintf(hmm, start, " ", end) line = fmt.Sprintf(hmm, start, " ", end)
} }
fmt.Fprintln(pf, line) newfile += fmt.Sprintln(line)
continue continue
} }
// find the end of the message // find the end of the message
if strings.HasPrefix(line, "}") { if strings.HasPrefix(line, "}") {
inMessage = false inMessage = false
// format and write the last message to the file
for _, newline := range formatMessage(curmsg) { for _, newline := range formatMessage(curmsg) {
fmt.Fprintln(pf, newline) newfile += fmt.Sprintln(newline)
} }
fmt.Fprintln(pf, line) newfile += fmt.Sprintln(line)
curmsg = nil curmsg = nil
continue continue
} }
// don't format or change anything when not in a "message {" section // don't format or change anything when not in a "message {" section
if !inMessage { if !inMessage {
fmt.Fprintln(pf, line) newfile += fmt.Sprintln(line)
continue continue
} }
curmsg = append(curmsg, line) curmsg = append(curmsg, line)
} }
pf, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
log.Info("file open error. permissions?", filename, err)
return err
}
newfile = strings.TrimSpace(newfile)
fmt.Fprintln(pf, newfile)
pf.Close()
// for i, s := range slices.Backward(pf.ToSort) { // for i, s := range slices.Backward(pf.ToSort) {
return nil return nil
} }
@ -100,8 +105,8 @@ func formatMessage(curmsg []string) []string {
for _, line := range curmsg { for _, line := range curmsg {
parts := strings.Split(line, ";") parts := strings.Split(line, ";")
if len(parts) < 2 { if len(parts) < 2 {
log.Info("parse error", line) // line is blank or just a comment
return curmsg continue
} }
vartype, varname, _, _ := tokenMsgVar(line) vartype, varname, _, _ := tokenMsgVar(line)
@ -114,6 +119,18 @@ func formatMessage(curmsg []string) []string {
} }
for _, line := range curmsg { for _, line := range curmsg {
line = strings.TrimSpace(line)
if line == "" {
newmsg = append(newmsg, line)
continue
}
if strings.HasPrefix(line, "//") {
pad := fmt.Sprintf("%d", maxVartype+maxVarname+21)
hmm := "%" + pad + "s %s"
line = fmt.Sprintf(hmm, " ", line) // todo: compute 50
newmsg = append(newmsg, line)
continue
}
mt := fmt.Sprintf("%d", maxVartype) mt := fmt.Sprintf("%d", maxVartype)
mv := fmt.Sprintf("%d", maxVarname) mv := fmt.Sprintf("%d", maxVarname)
@ -124,6 +141,7 @@ func formatMessage(curmsg []string) []string {
id = id + ";" id = id + ";"
newline := fmt.Sprintf(hmm, vartype, varname, id, end) newline := fmt.Sprintf(hmm, vartype, varname, id, end)
newline = strings.TrimRight(newline, " ")
newmsg = append(newmsg, newline) newmsg = append(newmsg, newline)
} }
return newmsg return newmsg