github.com/joomcode/pegomock@v2.9.2-0.20220414140958-14f53b6b2a6c+incompatible/pegomock/main.go (about)

     1  // Copyright 2016 Peter Goetz
     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 main
    16  
    17  import (
    18  	"io"
    19  	"os"
    20  	"path/filepath"
    21  	"strings"
    22  	"time"
    23  
    24  	kingpin "gopkg.in/alecthomas/kingpin.v2"
    25  
    26  	"github.com/joomcode/pegomock/pegomock/filehandling"
    27  	"github.com/petergtz/pegomock/pegomock/remove"
    28  	"github.com/petergtz/pegomock/pegomock/util"
    29  	"github.com/petergtz/pegomock/pegomock/watch"
    30  )
    31  
    32  var (
    33  	app = kingpin.New("pegomock", "Generates mocks based on interfaces.")
    34  )
    35  
    36  func main() {
    37  	Run(os.Args, os.Stderr, os.Stdin, app, make(chan bool))
    38  }
    39  
    40  func Run(cliArgs []string, out io.Writer, in io.Reader, app *kingpin.Application, done chan bool) {
    41  
    42  	workingDir, err := os.Getwd()
    43  	app.FatalIfError(err, "")
    44  
    45  	var (
    46  		generateCmd    = app.Command("generate", "Generate mocks based on the args provided. ")
    47  		destination    = generateCmd.Flag("output", "Output file; defaults to mock_<interface>_test.go.").Short('o').String()
    48  		destinationDir = generateCmd.Flag("output-dir", "Output directory; defaults to current directory. If set, package name defaults to this directory, unless explicitly overridden.").String()
    49  		mockNameOut    = generateCmd.Flag("mock-name", "Struct name of the generated mock; defaults to the interface prefixed with Mock").String()
    50  		packageOut     = generateCmd.Flag("package", "Package of the generated code; defaults to the package from which pegomock was executed suffixed with _test").String()
    51  		// TODO: self_package was taken as is from GoMock.
    52  		//       Still don't understand what it's really there for.
    53  		//       So for now it's not tested.
    54  		selfPackage            = generateCmd.Flag("self_package", "If set, the package this mock will be part of.").String()
    55  		debugParser            = generateCmd.Flag("debug", "Print debug information.").Short('d').Bool()
    56  		shouldGenerateMatchers = generateCmd.Flag("generate-matchers", "Generate matchers for all non built-in types in a \"matchers\" "+
    57  			"directory in the same directory where the mock file gets generated.").Short('m').Default("false").Bool()
    58  		matchersDestination = generateCmd.Flag("matchers-dir", "Generate matchers in the specified directory; defaults to "+
    59  			filepath.Join("<mockdir>", "matchers")).Short('p').String()
    60  		skipMatchers            = generateCmd.Flag("skip-matchers", "A list of types whose matchers shouldn't be generated").String()
    61  		useExperimentalModelGen = generateCmd.Flag("use-experimental-model-gen", "pegomock includes a new experimental source parser based on "+
    62  			"golang.org/x/tools/go/loader. It's currently experimental, but should be more powerful "+
    63  			"than the current reflect-based modelgen. E.g. reflect cannot detect method parameter names,"+
    64  			" and has to generate them based on a pattern. In a code editor with code assistence, this doesn't provide good help. "+
    65  			"\n\nThis option only works when specifying package path + interface, not with .go source files. Also, you can only specify *one* interface. This option cannot be used with the watch command.").Bool()
    66  		generateCmdArgs = generateCmd.Arg("args", "A (optional) Go package path + space-separated interface or a .go file").Required().Strings()
    67  
    68  		watchCmd       = app.Command("watch", "Watch over changes in interfaces and regenerate mocks if changes are detected.")
    69  		watchRecursive = watchCmd.Flag("recursive", "Recursively watch sub-directories as well.").Short('r').Bool()
    70  		watchPackages  = watchCmd.Arg("directories...", "One or more directories of Go packages to watch").Strings()
    71  
    72  		removeMocks          = app.Command("remove", "Remove mocks generated by Pegomock")
    73  		removeRecursive      = removeMocks.Flag("recursive", "Remove recursively in all sub-directories").Default("false").Short('r').Bool()
    74  		removeNonInteractive = removeMocks.Flag("non-interactive", "Don't ask for confirmation. Useful for scripts.").Default("false").Short('n').Bool()
    75  		removeDryRun         = removeMocks.Flag("dry-run", "Just show what would be done. Don't delete anything.").Default("false").Short('d').Bool()
    76  		removeSilent         = removeMocks.Flag("silent", "Don't write anything to standard out.").Default("false").Short('s').Bool()
    77  		removePath           = removeMocks.Arg("path", "Use as root directory instead of current working directory.").Default("").String()
    78  	)
    79  
    80  	app.Writer(out)
    81  	switch kingpin.MustParse(app.Parse(cliArgs[1:])) {
    82  
    83  	case generateCmd.FullCommand():
    84  		if err := util.ValidateArgs(*generateCmdArgs); err != nil {
    85  			app.FatalUsage(err.Error())
    86  		}
    87  		sourceArgs, err := util.SourceArgs(*generateCmdArgs)
    88  		if err != nil {
    89  			app.FatalUsage(err.Error())
    90  		}
    91  
    92  		if *destination != "" && *destinationDir != "" {
    93  			app.FatalUsage("Cannot use --output and --output-dir together")
    94  		}
    95  
    96  		realPackageOut := *packageOut
    97  		if *packageOut == "" {
    98  			realPackageOut, err = DeterminePackageNameIn(workingDir)
    99  			app.FatalIfError(err, "Could not determine package name.")
   100  		}
   101  
   102  		realDestination := *destination
   103  		realDestinationDir := workingDir
   104  		if *destinationDir != "" {
   105  			realDestinationDir, err = filepath.Abs(*destinationDir)
   106  			app.FatalIfError(err, "")
   107  			if *packageOut == "" {
   108  				realPackageOut = filepath.Base(*destinationDir)
   109  			}
   110  			if util.SourceMode(sourceArgs) {
   111  				realDestination = filepath.Join(*destinationDir, "mock_"+strings.TrimSuffix(sourceArgs[0], ".go")+".go")
   112  			} else {
   113  				realDestination = filepath.Join(*destinationDir, "mock_"+strings.ToLower(sourceArgs[len(sourceArgs)-1])+".go")
   114  			}
   115  		}
   116  
   117  		filehandling.GenerateMockFileInOutputDir(
   118  			sourceArgs,
   119  			realDestinationDir,
   120  			realDestination,
   121  			*mockNameOut,
   122  			realPackageOut,
   123  			*selfPackage,
   124  			*debugParser,
   125  			out,
   126  			*useExperimentalModelGen,
   127  			*shouldGenerateMatchers,
   128  			*matchersDestination,
   129  			*skipMatchers)
   130  
   131  	case watchCmd.FullCommand():
   132  		var targetPaths []string
   133  		if len(*watchPackages) == 0 {
   134  			targetPaths = []string{workingDir}
   135  		} else {
   136  			targetPaths = *watchPackages
   137  		}
   138  		watch.CreateWellKnownInterfaceListFilesIfNecessary(targetPaths)
   139  		util.Ticker(watch.NewMockFileUpdater(targetPaths, *watchRecursive).Update, 2*time.Second, done)
   140  
   141  	case removeMocks.FullCommand():
   142  		path := *removePath
   143  		if path == "" {
   144  			var e error
   145  			path, e = os.Getwd()
   146  			app.FatalIfError(e, "Could not get current working directory")
   147  		}
   148  		remove.Remove(path, *removeRecursive, !*removeNonInteractive, *removeDryRun, *removeSilent, out, in, os.Remove)
   149  	}
   150  }