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 }