diff --git a/cmd/goreorder/commands.go b/cmd/goreorder/commands.go index 28ed2ad..73a95a1 100644 --- a/cmd/goreorder/commands.go +++ b/cmd/goreorder/commands.go @@ -3,6 +3,7 @@ package main import ( "errors" "fmt" + "io" "os" "os/exec" "path/filepath" @@ -14,7 +15,20 @@ import ( "github.com/spf13/viper" ) +var ( + version = "master" // changed at compilation time + defaultOutpout io.Writer = os.Stdout // default output is stdout + defaultErrOutpout io.Writer = os.Stderr // default error output is stderr +) + func buildCompletionCommand() *cobra.Command { + var completionExamples = []string{ + "$ %[1]s completion bash", + "$ %[1]s completion bash -no-documentation", + "$ %[1]s completion zsh", + "$ %[1]s completion fish", + "$ %[1]s completion powershell", + } noDocumentation := false bashv1Completion := false completionCmd := &cobra.Command{ @@ -56,6 +70,14 @@ func buildCompletionCommand() *cobra.Command { } func buildMainCommand() *cobra.Command { + const usage = `%[1]s reorders the types, methods... in a Go +source file. By default, it will print the result to stdout. To allow %[1]s +to write to the file, use the -write flag.` + var examples = []string{ + "$ %[1]s reorder --write --reorder-types --format gofmt file.go", + "$ %[1]s reorder --diff ./mypackage", + "$ cat file.go | %[1]s reorder", + } cmd := cobra.Command{ Use: "goreorder [flags] [file.go|directory|stdin]", @@ -71,6 +93,10 @@ func buildMainCommand() *cobra.Command { }, } + // my god this is so cool... + cmd.SetOut(defaultOutpout) + cmd.SetErr(defaultErrOutpout) + config := &ReorderConfig{ FormatToolName: "gofmt", Write: false, @@ -136,8 +162,7 @@ func buildReorderCommand(config *ReorderConfig) *cobra.Command { return fmt.Errorf("The executable '" + config.FormatToolName + "' does not exist") } logger.SetVerbose(config.Verbose) - run(config, args...) - return nil + return reorder(config, args...) }, } diff --git a/cmd/goreorder/config_test.go b/cmd/goreorder/config_test.go index 05a8a01..c6eb837 100644 --- a/cmd/goreorder/config_test.go +++ b/cmd/goreorder/config_test.go @@ -10,6 +10,11 @@ import ( "gopkg.in/yaml.v3" ) +func init() { + defaultOutpout = bytes.NewBuffer([]byte{}) + defaultErrOutpout = bytes.NewBuffer([]byte{}) +} + func TestNoConfigFile(t *testing.T) { defaultOutpout = bytes.NewBuffer([]byte{}) printConfigFile(&ReorderConfig{ diff --git a/cmd/goreorder/main.go b/cmd/goreorder/main.go index 90898fb..109b72c 100644 --- a/cmd/goreorder/main.go +++ b/cmd/goreorder/main.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "fmt" "io" "io/ioutil" @@ -12,37 +13,18 @@ import ( "github.com/metal3d/goreorder/ordering" ) -const ( - usage = `%[1]s reorders the types, methods... in a Go -source file. By default, it will print the result to stdout. To allow %[1]s -to write to the file, use the -write flag.` -) - var ( - version = "master" // changed at compilation time - log = logger.GetLogger() - examples = []string{ - "$ %[1]s reorder --write --reorder-types --format gofmt file.go", - "$ %[1]s reorder --diff ./mypackage", - "$ cat file.go | %[1]s reorder", - } - completionExamples = []string{ - "$ %[1]s completion bash", - "$ %[1]s completion bash -no-documentation", - "$ %[1]s completion zsh", - "$ %[1]s completion fish", - "$ %[1]s completion powershell", - } - defaultOutpout io.Writer = os.Stdout + log = logger.GetLogger() ) func main() { if err := buildMainCommand().Execute(); err != nil { - fmt.Println(fmt.Errorf("%v", err)) + io.WriteString(defaultErrOutpout, fmt.Sprintf("%s\n", err)) os.Exit(1) } } +// ReorderConfig is the configuration for the reorder command type ReorderConfig struct { FormatToolName string `yaml:"format"` Write bool `yaml:"write"` @@ -52,10 +34,40 @@ type ReorderConfig struct { DefOrder []string `yaml:"order"` } -func processFile(fileOrDirectoryName string, input []byte, config *ReorderConfig) { +func reorder(config *ReorderConfig, args ...string) error { + + // is there something in stdin? + filename := "" + var input []byte + var err error + stat, _ := os.Stdin.Stat() + if (stat.Mode() & os.ModeCharDevice) == 0 { + // read from stdin + input, err = ioutil.ReadAll(os.Stdin) + if err != nil { + return fmt.Errorf("Error while reading stdin: %w", err) + } + filename = "stdin.go" + config.Write = false + log.Println("Processing stdin, write is set to false") + } else { + // read from file or directory + filename = args[0] + if filename == "" { + return fmt.Errorf("Filename is empty") + } + _, err := os.Stat(filename) + if err != nil { + return fmt.Errorf("Error while getting file stat: %w", err) + } + } + + return processFile(filename, input, config) +} + +func processFile(fileOrDirectoryName string, input []byte, config *ReorderConfig) error { if strings.HasSuffix(fileOrDirectoryName, "_test.go") { - log.Println("Skipping test file: " + fileOrDirectoryName) - return + return fmt.Errorf("Skipping test file: " + fileOrDirectoryName) } if input != nil && len(input) != 0 { @@ -68,36 +80,32 @@ func processFile(fileOrDirectoryName string, input []byte, config *ReorderConfig Src: input, }) if err != nil { - log.Fatal(err) + return fmt.Errorf("Error while reordering source: %w", err) } fmt.Print(string(content)) - return + return nil } stat, err := os.Stat(fileOrDirectoryName) if err != nil { - log.Fatal(err) - return + return fmt.Errorf("Error while getting file stat: %w", err) } if stat.IsDir() { // skip vendor directory if strings.HasSuffix(fileOrDirectoryName, "vendor") { - log.Println("Skipping vendor directory: " + fileOrDirectoryName) - return + return fmt.Errorf("Skipping vendor directory: " + fileOrDirectoryName) } // get all files in directory and process them log.Println("Processing directory: " + fileOrDirectoryName) - filepath.Walk(fileOrDirectoryName, func(path string, info os.FileInfo, err error) error { + return filepath.Walk(fileOrDirectoryName, func(path string, info os.FileInfo, err error) error { if err != nil { - log.Fatal(err) - return err + return fmt.Errorf("Error while walking directory: %w", err) } if strings.HasSuffix(path, ".go") { processFile(path, nil, config) } return nil }) - return } log.Println("Processing file: " + fileOrDirectoryName) @@ -110,47 +118,16 @@ func processFile(fileOrDirectoryName string, input []byte, config *ReorderConfig Src: input, }) if err != nil { - log.Println("ERR: Ordering error:", err) - return + return fmt.Errorf("Error while reordering file: %w", err) } if config.Write { err = ioutil.WriteFile(fileOrDirectoryName, []byte(output), 0644) if err != nil { - log.Fatal("ERR: Write to file failed:", err) - } - } else { - fmt.Println(output) - } -} - -func run(config *ReorderConfig, args ...string) { - - // is there something in stdin? - filename := "" - var input []byte - var err error - stat, _ := os.Stdin.Stat() - if (stat.Mode() & os.ModeCharDevice) == 0 { - // read from stdin - input, err = ioutil.ReadAll(os.Stdin) - if err != nil { - log.Fatal(err) + return fmt.Errorf("Error while writing to file: %w", err) } - filename = "stdin.go" - config.Write = false - log.Println("Processing stdin, write is set to false") } else { - // read from file or directory - filename = args[0] - if filename == "" { - log.Println("filename is empty") - os.Exit(1) - } - _, err := os.Stat(filename) - if err != nil { - log.Fatal(err) - } + //fmt.Println(output) + io.Copy(defaultOutpout, bytes.NewBufferString(output)) } - - processFile(filename, input, config) + return nil } diff --git a/cmd/goreorder/main_test.go b/cmd/goreorder/main_test.go index f647e75..b1e01d0 100644 --- a/cmd/goreorder/main_test.go +++ b/cmd/goreorder/main_test.go @@ -8,6 +8,11 @@ import ( "testing" ) +func init() { + defaultOutpout = bytes.NewBuffer([]byte{}) + defaultErrOutpout = bytes.NewBuffer([]byte{}) +} + func TestBuildCommand(t *testing.T) { cmd := buildMainCommand() if cmd == nil { @@ -138,9 +143,6 @@ func TestNoArgs(t *testing.T) { } func TestCompletionCommands(t *testing.T) { - - defaultOutpout = &bytes.Buffer{} - for _, shell := range []string{"bash", "zsh", "fish", "powershell"} { cmd := buildMainCommand() cmd.SetArgs([]string{"completion", shell}) diff --git a/ordering/main.go b/ordering/main.go index be7b2d5..8996637 100644 --- a/ordering/main.go +++ b/ordering/main.go @@ -12,35 +12,12 @@ import ( "strings" ) -const ( - Const Order = "const" - Init Order = "init" - Main Order = "main" - Var Order = "var" - Interface Order = "interface" - Type Order = "type" - Func Order = "func" -) - // DefaultOrder is the default order of elements. // // Note, Init and Main are not in the list. If they are present, the init and main functions // will be moved. var DefaultOrder = []Order{Const, Var, Interface, Type, Func} -// Order is the type of order, it's an alias of string. -type Order = string - -// ReorderConfig is the configuration for the reorder function. -type ReorderConfig struct { - Filename string - FormatCommand string - ReorderStructs bool - Diff bool - Src interface{} - DefOrder []Order -} - func formatWithCommand(content []byte, output string, opt ReorderConfig) (newcontent []byte, err error) { // we use the format command given by the user // on a temporary file we need to create and remove diff --git a/ordering/parser.go b/ordering/parser.go index 489a61d..640c762 100644 --- a/ordering/parser.go +++ b/ordering/parser.go @@ -9,31 +9,6 @@ import ( "strings" ) -// GoType represents a struct, method or constructor. The "SourceCode" field contains the doc comment and source in Go, formated and ready to be injected in the source file. -type GoType struct { - // Name of the struct, method or constructor - Name string - // SourceCode contains the doc comment and source in Go, formated and ready to be injected in the source file. - SourceCode string - // OpeningLine is the line number where the struct, method or constructor starts in the source file. - OpeningLine int - // ClosingLine is the line number where the struct, method or constructor ends in the source file. - ClosingLine int -} - -// ParsedInfo contains information we need to sort in the source file. -type ParsedInfo struct { - Functions map[string]*GoType - Methods map[string][]*GoType - Constructors map[string][]*GoType - Types map[string]*GoType - Interfaces map[string]*GoType - Constants map[string]*GoType - Variables map[string]*GoType - TypeNames *StingList - InterfaceNames *StingList -} - // Parse the given file and return the methods, constructors and structs. func Parse(filename string, src interface{}) (*ParsedInfo, error) { fset := token.NewFileSet() diff --git a/ordering/types.go b/ordering/types.go new file mode 100644 index 0000000..bef9874 --- /dev/null +++ b/ordering/types.go @@ -0,0 +1,52 @@ +package ordering + +const ( + Const Order = "const" + Init Order = "init" + Main Order = "main" + Var Order = "var" + Interface Order = "interface" + Type Order = "type" + Func Order = "func" +) + +// GoType represents a struct, method or constructor. The "SourceCode" field contains the doc comment and source in Go, formated and ready to be injected in the source file. +type GoType struct { + // Name of the struct, method or constructor + Name string + + // SourceCode contains the doc comment and source in Go, formated and ready to be injected in the source file. + SourceCode string + + // OpeningLine is the line number where the struct, method or constructor starts in the source file. + OpeningLine int + + // ClosingLine is the line number where the struct, method or constructor ends in the source file. + ClosingLine int +} + +// Order is the type of order, it's an alias of string. +type Order = string + +// ParsedInfo contains information we need to sort in the source file. +type ParsedInfo struct { + Functions map[string]*GoType + Methods map[string][]*GoType + Constructors map[string][]*GoType + Types map[string]*GoType + Interfaces map[string]*GoType + Constants map[string]*GoType + Variables map[string]*GoType + TypeNames *StingList + InterfaceNames *StingList +} + +// ReorderConfig is the configuration for the reorder function. +type ReorderConfig struct { + Filename string + FormatCommand string + ReorderStructs bool + Diff bool + Src interface{} + DefOrder []Order +}