github.com/petergtz/pegomock@v2.9.1-0.20230424204322-eb0e044013df+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/petergtz/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  		useExperimentalModelGen = generateCmd.Flag("use-experimental-model-gen", "pegomock includes a new experimental source parser based on "+
    61  			"golang.org/x/tools/go/loader. It's currently experimental, but should be more powerful "+
    62  			"than the current reflect-based modelgen. E.g. reflect cannot detect method parameter names,"+
    63  			" and has to generate them based on a pattern. In a code editor with code assistence, this doesn't provide good help. "+
    64  			"\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()
    65  		generateCmdArgs = generateCmd.Arg("args", "A (optional) Go package path + space-separated interface or a .go file").Required().Strings()
    66  
    67  		watchCmd       = app.Command("watch", "Watch over changes in interfaces and regenerate mocks if changes are detected.")
    68  		watchRecursive = watchCmd.Flag("recursive", "Recursively watch sub-directories as well.").Short('r').Bool()
    69  		watchPackages  = watchCmd.Arg("directories...", "One or more directories of Go packages to watch").Strings()
    70  
    71  		removeMocks          = app.Command("remove", "Remove mocks generated by Pegomock")
    72  		removeRecursive      = removeMocks.Flag("recursive", "Remove recursively in all sub-directories").Default("false").Short('r').Bool()
    73  		removeNonInteractive = removeMocks.Flag("non-interactive", "Don't ask for confirmation. Useful for scripts.").Default("false").Short('n').Bool()
    74  		removeDryRun         = removeMocks.Flag("dry-run", "Just show what would be done. Don't delete anything.").Default("false").Short('d').Bool()
    75  		removeSilent         = removeMocks.Flag("silent", "Don't write anything to standard out.").Default("false").Short('s').Bool()
    76  		removePath           = removeMocks.Arg("path", "Use as root directory instead of current working directory.").Default("").String()
    77  	)
    78  
    79  	app.Writer(out)
    80  	switch kingpin.MustParse(app.Parse(cliArgs[1:])) {
    81  
    82  	case generateCmd.FullCommand():
    83  		if err := util.ValidateArgs(*generateCmdArgs); err != nil {
    84  			app.FatalUsage(err.Error())
    85  		}
    86  		sourceArgs, err := util.SourceArgs(*generateCmdArgs)
    87  		if err != nil {
    88  			app.FatalUsage(err.Error())
    89  		}
    90  
    91  		if *destination != "" && *destinationDir != "" {
    92  			app.FatalUsage("Cannot use --output and --output-dir together")
    93  		}
    94  
    95  		realPackageOut := *packageOut
    96  		if *packageOut == "" {
    97  			realPackageOut, err = DeterminePackageNameIn(workingDir)
    98  			app.FatalIfError(err, "Could not determine package name.")
    99  		}
   100  
   101  		realDestination := *destination
   102  		realDestinationDir := workingDir
   103  		if *destinationDir != "" {
   104  			realDestinationDir, err = filepath.Abs(*destinationDir)
   105  			app.FatalIfError(err, "")
   106  			if *packageOut == "" {
   107  				realPackageOut = filepath.Base(*destinationDir)
   108  			}
   109  			if util.SourceMode(sourceArgs) {
   110  				realDestination = filepath.Join(*destinationDir, "mock_"+strings.TrimSuffix(sourceArgs[0], ".go")+".go")
   111  			} else {
   112  				realDestination = filepath.Join(*destinationDir, "mock_"+strings.ToLower(sourceArgs[len(sourceArgs)-1])+".go")
   113  			}
   114  		}
   115  
   116  		filehandling.GenerateMockFileInOutputDir(
   117  			sourceArgs,
   118  			realDestinationDir,
   119  			realDestination,
   120  			*mockNameOut,
   121  			realPackageOut,
   122  			*selfPackage,
   123  			*debugParser,
   124  			out,
   125  			*useExperimentalModelGen,
   126  			*shouldGenerateMatchers,
   127  			*matchersDestination)
   128  
   129  	case watchCmd.FullCommand():
   130  		var targetPaths []string
   131  		if len(*watchPackages) == 0 {
   132  			targetPaths = []string{workingDir}
   133  		} else {
   134  			targetPaths = *watchPackages
   135  		}
   136  		watch.CreateWellKnownInterfaceListFilesIfNecessary(targetPaths)
   137  		util.Ticker(watch.NewMockFileUpdater(targetPaths, *watchRecursive).Update, 2*time.Second, done)
   138  
   139  	case removeMocks.FullCommand():
   140  		path := *removePath
   141  		if path == "" {
   142  			var e error
   143  			path, e = os.Getwd()
   144  			app.FatalIfError(e, "Could not get current working directory")
   145  		}
   146  		remove.Remove(path, *removeRecursive, !*removeNonInteractive, *removeDryRun, *removeSilent, out, in, os.Remove)
   147  	}
   148  }