From 0a3aa9230bb4d316144a610d9010d24674c79138 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Mon, 13 Jan 2025 02:50:01 -0600 Subject: [PATCH] start a help system for forge --- Makefile | 15 ++++++ human.go | 67 ++++++++++++++++++++++++++ protoc.go | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 Makefile create mode 100644 human.go create mode 100644 protoc.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2e779c1 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +# A simple, generic help repo for forge + +all: goimports vet + +vet: + @GO111MODULE=off go vet + @echo this go library package builds okay + +# autofixes your import headers in your golang files +goimports: + goimports -w *.go + +clean: + rm -f *.pb.go + -rm -f go.* diff --git a/human.go b/human.go new file mode 100644 index 0000000..79780ee --- /dev/null +++ b/human.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + + "go.wit.com/log" +) + +func CheckProtoc() bool { + if checkCmdSimple("protoc") { + userInstructions() + log.Sleep(2) + return false + } + if checkCmdSimple("protoc-gen-go") { + userInstructions() + log.Sleep(2) + return false + } + return true +} + +func checkCmdSimple(cmd string) bool { + path, err := exec.LookPath(cmd) + if err != nil { + fmt.Printf("\n%s is not in the PATH\n", cmd) + return false + } else { + fmt.Printf("%s is available at %s\n", cmd, path) + } + return true +} + +func checkCmd(cmd string) (string, error) { + path, err := exec.LookPath(cmd) + if err != nil { + fmt.Printf("\n%s is not in the PATH\n", cmd) + } else { + fmt.Printf("%s is available at %s\n", cmd, path) + } + return path, err +} + +func oldcheckCmd(cmd string) { + // path, err := exec.LookPath(cmd) + _, err := exec.LookPath(cmd) + if err != nil { + // fmt.Printf("\n%s is not in the PATH\n", cmd) + userInstructions() + log.Sleep(2) + } else { + // fmt.Printf("%s is available at %s\n", cmd, path) + } +} + +// todo: figure out how to determine, in a package, what the program name is +func okExit(s string) { + log.Info(" ok", s) + os.Exit(0) +} + +func badExit(err error) { + log.Info(" error:", err) + os.Exit(-1) +} diff --git a/protoc.go b/protoc.go new file mode 100644 index 0000000..e27bd2c --- /dev/null +++ b/protoc.go @@ -0,0 +1,139 @@ +package main + +// auto run protoc with the correct args + +import ( + "bufio" + "fmt" + "os" + "strings" + + "github.com/google/uuid" + "go.wit.com/log" + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +/* +This verifies a .proto file conforms to the autogenpb standard + +That means, for "toy.proto", there MUST exist: + +message toy { +} +message toys { +} + +this parses the .proto file and handles anything with `autogenpb: ` +does the fruit.proto file have "message Fruits" +does it have a Uuid & valid version ? + +This returns (UUID, Version, error on failure) +*/ +func ValidProtobuf(filename string) (string, string, error) { + filebase := strings.TrimSuffix(filename, ".proto") + base := cases.Title(language.English, cases.NoLower).String(filebase) + pluralBase := base + "s" // to conform to autogenpb, it must have an added 's' + + file, err := os.Open(filename) + if err != nil { + return "", "", err + } + defer file.Close() + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + + prefix := "message " + pluralBase + " {" + if !strings.HasPrefix(line, prefix) { + // log.Info("nope", prefix, "line", line) + // nope, not this line + continue + } + + scanner.Scan() + line = scanner.Text() + fields := strings.Fields(line) + // log.Info("GOT LINE", line) + if fields[0] == "string" && fields[1] != "uuid" { + noUuid(filename) + return "", "", fmt.Errorf("proto file does not have a UUID") + } + // ok, uuid is here + UUID := line + log.Info("found UUID:", line) + + scanner.Scan() + line = scanner.Text() + fields = strings.Fields(line) + // log.Info("GOT LINE", line) + if fields[0] == "string" && fields[1] != "version" { + noUuid(filename) + return "", "", fmt.Errorf("proto file does not have a version") + } + // found "version", the .proto file conforms + version := line + log.Info("found Version:", line) + return UUID, version, nil + } + // right now, noPluralMessage does os.Exit(-1) + // return "", "", noPluralMessage(filename) + return "", "", fmt.Errorf("proto file does not have message %s", pluralBase) +} + +func noPluralMessage(filename string) { + filebase := strings.TrimSuffix(filename, ".proto") + base := cases.Title(language.English, cases.NoLower).String(filebase) + + log.Info("") + log.Info("###########################################################################") + log.Info("Your proto file", filename, "does not contain the correct 'message' definitions") + log.Info("") + log.Info("For autogenpb to work on your file", 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", 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 noUuid(filename string) { + filebase := strings.TrimSuffix(filename, ".proto") + base := cases.Title(language.English, cases.NoLower).String(filebase) + id := uuid.New() + + log.Info("") + log.Info("###########################################################################") + log.Info("Your proto file", filename, "is incorrect for 'message' ", base+"s") + log.Info("") + log.Info("For autogenpb to work on your file", filename, ",", base+"s must be exactly:") + log.Info("") + log.Info("message", base+"s", "{ // `autogenpb:marshal` `autogenpb:mutex`") + log.Info(" string uuid = 1; // `autogenpb:uuid:" + id.String() + "`") + log.Info(" string version = 2; // `autogenpb:version:v0.0.1`") + log.Info(" repeated", base, base+"s", " = 3; // THIS MUST BE ", base, " and then ", base+"s") + 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", filename)) +} + +func userInstructions() { + log.Info("This is likely because you don't have protoc and protoc-gen-go installed") + log.Info("") + log.Info("On debian, you can:") + log.Info(" apt install protobuf-compiler # for protoc") + log.Info(" apt install protoc-gen-go # for protoc-gen-go") + log.Info("") +}