github.com/jdhenke/godel@v0.0.0-20161213181855-abeb3861bf0d/apps/gonform/cmd/format.go (about)

     1  // Copyright 2016 Palantir Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cmd
    16  
    17  import (
    18  	"bytes"
    19  	"io"
    20  	"path"
    21  
    22  	"github.com/nmiyake/pkg/dirs"
    23  	"github.com/palantir/amalgomate/amalgomated"
    24  	"github.com/palantir/pkg/cli"
    25  	"github.com/palantir/pkg/cli/cfgcli"
    26  	"github.com/palantir/pkg/cli/flag"
    27  	"github.com/palantir/pkg/matcher"
    28  	"github.com/pkg/errors"
    29  
    30  	"github.com/palantir/godel/apps/gonform/config"
    31  	"github.com/palantir/godel/apps/gonform/params"
    32  )
    33  
    34  var (
    35  	goFmt     = Library.MustNewCmd("gofmt")
    36  	ptImports = Library.MustNewCmd("ptimports")
    37  )
    38  
    39  func RunAllCommand(supplier amalgomated.CmderSupplier) cli.Command {
    40  	return cli.Command{
    41  		Name:  "runAll",
    42  		Usage: "Run all format commands on Go files",
    43  		Action: func(ctx cli.Context) error {
    44  			wd, err := dirs.GetwdEvalSymLinks()
    45  			if err != nil {
    46  				return err
    47  			}
    48  			return DoRunAll(ctx.Slice(filesParamName), ctx, supplier, wd)
    49  		},
    50  		Flags: []flag.Flag{
    51  			FilesParam,
    52  		},
    53  	}
    54  }
    55  
    56  func GoFmtCommand(supplier amalgomated.CmderSupplier) cli.Command {
    57  	return formatCommand(goFmt, "Run ptimports on Go files", supplier)
    58  }
    59  
    60  func PTImportsCommand(supplier amalgomated.CmderSupplier) cli.Command {
    61  	return formatCommand(ptImports, "Run ptimports on Go files", supplier)
    62  }
    63  
    64  func DoRunAll(filesParam []string, ctx cli.Context, supplier amalgomated.CmderSupplier, wd string) error {
    65  	cmds := []amalgomated.Cmd{
    66  		goFmt,
    67  		ptImports,
    68  	}
    69  	params, err := createParams(filesParam, ctx, wd)
    70  	if err != nil {
    71  		return err
    72  	}
    73  	for _, currCmd := range cmds {
    74  		err := doFormat(currCmd, params, ctx, supplier, wd)
    75  		if err != nil {
    76  			return err
    77  		}
    78  	}
    79  	return nil
    80  }
    81  
    82  func formatCommand(cmd amalgomated.Cmd, usage string, supplier amalgomated.CmderSupplier) cli.Command {
    83  	return cli.Command{
    84  		Name:  cmd.Name(),
    85  		Usage: usage,
    86  		Action: func(ctx cli.Context) error {
    87  			wd, err := dirs.GetwdEvalSymLinks()
    88  			if err != nil {
    89  				return err
    90  			}
    91  			return doSingleFormat(cmd, ctx.Slice(filesParamName), ctx, supplier, wd)
    92  		},
    93  		Flags: []flag.Flag{
    94  			FilesParam,
    95  		},
    96  	}
    97  }
    98  
    99  type formatterParams struct {
   100  	Formatters map[string]params.Formatter
   101  	Files      []string
   102  	List       bool
   103  	Verbose    bool
   104  }
   105  
   106  func doSingleFormat(cmd amalgomated.Cmd, filesParam []string, ctx cli.Context, supplier amalgomated.CmderSupplier, wd string) error {
   107  	params, err := createParams(filesParam, ctx, wd)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	return doFormat(cmd, params, ctx, supplier, wd)
   112  }
   113  
   114  func createParams(filesParam []string, ctx cli.Context, wd string) (formatterParams, error) {
   115  	cfg, err := config.Load(cfgcli.ConfigPath, cfgcli.ConfigJSON)
   116  	if err != nil {
   117  		return formatterParams{}, errors.Wrapf(err, "failed to load configuration")
   118  	}
   119  
   120  	files, err := getFiles(filesParam, cfg.Exclude, wd)
   121  	if err != nil {
   122  		return formatterParams{}, errors.Wrapf(err, "failed to get files")
   123  	}
   124  
   125  	return formatterParams{
   126  		Formatters: cfg.Formatters,
   127  		Files:      files,
   128  		List:       ctx.Bool(listFlagName),
   129  		Verbose:    ctx.Bool(verboseFlagName),
   130  	}, nil
   131  }
   132  
   133  func doFormat(cmd amalgomated.Cmd, params formatterParams, ctx cli.Context, supplier amalgomated.CmderSupplier, wd string) error {
   134  	if len(params.Files) == 0 {
   135  		return nil
   136  	}
   137  
   138  	supplier = amalgomated.SupplierWithPrependedArgs(supplier, func(cmd amalgomated.Cmd) []string {
   139  		formatArg := "-w"
   140  		if params.List {
   141  			formatArg = "-l"
   142  		}
   143  		return append(params.Formatters[cmd.Name()].Args, formatArg)
   144  	})
   145  
   146  	combinedBuf := bytes.Buffer{}
   147  	stdoutMultiWriter := io.MultiWriter(ctx.App.Stdout, &combinedBuf)
   148  	stderrMultiWriter := io.MultiWriter(ctx.App.Stderr, &combinedBuf)
   149  
   150  	cmder, err := supplier(cmd)
   151  	if err != nil {
   152  		return errors.Wrapf(err, "failed to create command %s", cmd.Name())
   153  	}
   154  
   155  	if params.Verbose {
   156  		ctx.Printf("Running %s...\n", cmd.Name())
   157  	}
   158  	execCmd := cmder.Cmd(params.Files, wd)
   159  	execCmd.Stdout = stdoutMultiWriter
   160  	execCmd.Stderr = stderrMultiWriter
   161  
   162  	if err := execCmd.Run(); err != nil {
   163  		return errors.Wrapf(err, "command %s failed", cmd.Name())
   164  	} else if params.List && len(combinedBuf.Bytes()) != 0 {
   165  		if params.Verbose {
   166  			return errors.Errorf("%s failed: unformatted Go files found", cmd.Name())
   167  		}
   168  		return errors.Errorf("")
   169  	}
   170  	return nil
   171  }
   172  
   173  func getFiles(filesParam []string, exclude matcher.Matcher, wd string) ([]string, error) {
   174  	var files []string
   175  	var err error
   176  	if len(filesParam) == 0 {
   177  		// exclude entries specified by the configuration
   178  		files, err = matcher.ListFiles(wd, matcher.Name(`.*\.go`), exclude)
   179  		if err != nil {
   180  			return nil, errors.Wrapf(err, "failed to list *.go files in %d", wd)
   181  		}
   182  	} else {
   183  		// filter arguments based on exclude config
   184  		files = make([]string, 0, len(filesParam))
   185  		for _, currPath := range filesParam {
   186  			if !exclude.Match(currPath) {
   187  				files = append(files, currPath)
   188  			}
   189  
   190  		}
   191  	}
   192  	files = toAbsPaths(wd, files)
   193  
   194  	if len(files) == 0 {
   195  		// no files to process after exclusions applied
   196  		return nil, nil
   197  	}
   198  
   199  	return files, nil
   200  }
   201  
   202  func toAbsPaths(basePath string, relPaths []string) []string {
   203  	paths := make([]string, len(relPaths))
   204  	for i, currPath := range relPaths {
   205  		paths[i] = path.Join(basePath, currPath)
   206  	}
   207  	return paths
   208  }