From b6295fa852ea49c7025536dd16d961f481ee8880 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Tue, 8 Oct 2019 03:40:04 -0400 Subject: [PATCH] Better error reporting (#3) Output better error reporting when a package cant be loaded, rather than output a blank *.proto file. The package.Load() error did not contain any useful information about failed package loads, they must be fetched at a lower level. Additionally added some more documentation, hopefully to make it easier for new users to use this. --- README.md | 15 +++++++++++- main.go | 68 ++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 6f4c846..7cdfc05 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,24 @@ Generate Protobuf messages from given go structs. No RPC, not gogo syntax, just pure Protobuf messages. +### Syntax +``` +-f string + Protobuf output file path. (default ".") +-filter string + Filter by struct names. Case insensitive. +-p value + Fully qualified path of packages to analyse. Relative paths ("./example/in") are allowed. +``` + ### Example +Your package you wish to export must be inside of your working directory. Package paths can be fully-qualified or relative. + ```sh GO111MODULE=off go get -u github.com/anjmao/go2proto -go2proto -f ${PWD}/example/out -p github.com/anjmao/go2proto/example/in +cd ~/go/src/github.com/anjmao/go2proto +go2proto -f ./example/out -p ./example/in ``` ### Note diff --git a/main.go b/main.go index 2c2637b..965951c 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,9 @@ package main import ( + "errors" "flag" + "fmt" "go/token" "go/types" "log" @@ -18,6 +20,8 @@ import ( type arrFlags []string +const outputFileName = "output.proto" + func (i *arrFlags) String() string { return "" } @@ -28,46 +32,46 @@ func (i *arrFlags) Set(value string) error { } var ( - filter = flag.String("filter", "", "Filter struct names.") - protoFolder = flag.String("f", "", "Proto output path.") - pkgFlags arrFlags + filter = flag.String("filter", "", "Filter by struct names. Case insensitive.") + targetFolder = flag.String("f", ".", "Protobuf output file path.") + pkgFlags arrFlags ) func main() { - flag.Var(&pkgFlags, "p", "Go source packages.") + flag.Var(&pkgFlags, "p", `Fully qualified path of packages to analyse. Relative paths ("./example/in") are allowed.`) flag.Parse() - if len(pkgFlags) == 0 || protoFolder == nil { - flag.PrintDefaults() - os.Exit(1) + pwd, err := os.Getwd() + if err != nil { + log.Fatalf("error getting working directory: %s", err) } - if err := checkOutFolder(*protoFolder); err != nil { - log.Fatal(err) + if len(pkgFlags) == 0 { + flag.PrintDefaults() + os.Exit(1) } - pwd, err := os.Getwd() + //ensure the path exists + _, err = os.Stat(*targetFolder) if err != nil { - log.Fatal(err) + log.Fatalf("error getting output file: %s", err) } pkgs, err := loadPackages(pwd, pkgFlags) if err != nil { - log.Fatal(err) + log.Fatalf("error fetching packages: %s", err) } - msgs := getMessages(pkgs, *filter) + msgs := getMessages(pkgs, strings.ToLower(*filter)) - if err := writeOutput(msgs, *protoFolder); err != nil { - log.Fatal(err) + if err = writeOutput(msgs, *targetFolder); err != nil { + log.Fatalf("error writing output: %s", err) } -} -func checkOutFolder(path string) error { - _, err := os.Stat(path) - return err + log.Printf("output file written to %s%s%s\n", pwd, string(os.PathSeparator), outputFileName) } +// attempt to load all packages func loadPackages(pwd string, pkgs []string) ([]*packages.Package, error) { fset := token.NewFileSet() cfg := &packages.Config{ @@ -75,7 +79,25 @@ func loadPackages(pwd string, pkgs []string) ([]*packages.Package, error) { Mode: packages.LoadSyntax, Fset: fset, } - return packages.Load(cfg, pkgs...) + packages, err := packages.Load(cfg, pkgs...) + if err != nil { + return nil, err + } + var errs = "" + //check each loaded package for errors during loading + for _, p := range packages { + if len(p.Errors) > 0 { + errs += fmt.Sprintf("error fetching package %s: ", p.String()) + for _, e := range p.Errors { + errs += e.Error() + } + errs += "; " + } + } + if errs != "" { + return nil, errors.New(errs) + } + return packages, nil } type message struct { @@ -106,7 +128,7 @@ func getMessages(pkgs []*packages.Package, filter string) []*message { } if s, ok := t.Type().Underlying().(*types.Struct); ok { seen[t.Name()] = struct{}{} - if filter == "" || strings.Contains(t.Name(), filter) { + if filter == "" || strings.Contains(strings.ToLower(t.Name()), filter) { out = appendMessage(out, t, s) } } @@ -214,9 +236,9 @@ message {{.Name}} { panic(err) } - f, err := os.Create(filepath.Join(path, "output.proto")) + f, err := os.Create(filepath.Join(path, outputFileName)) if err != nil { - return err + return fmt.Errorf("unable to create file %s : %s", outputFileName, err) } defer f.Close()