github.com/djui/moq@v0.3.3/main.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "errors" 6 "flag" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 13 "github.com/djui/moq/pkg/moq" 14 ) 15 16 // Version is the command version, injected at build time. 17 var Version string = "dev" 18 19 type userFlags struct { 20 outFile string 21 pkgName string 22 formatter string 23 stubImpl bool 24 skipEnsure bool 25 remove bool 26 force bool 27 verbose bool 28 args []string 29 } 30 31 func main() { 32 var flags userFlags 33 flag.StringVar(&flags.outFile, "out", "", "output file (default stdout)") 34 flag.StringVar(&flags.pkgName, "pkg", "", "package name (default will infer)") 35 flag.StringVar(&flags.formatter, "fmt", "", "go pretty-printer: gofmt, goimports or noop (default gofmt)") 36 flag.BoolVar(&flags.stubImpl, "stub", false, 37 "return zero values when no mock implementation is provided, do not panic") 38 printVersion := flag.Bool("version", false, "show the version for moq") 39 flag.BoolVar(&flags.skipEnsure, "skip-ensure", false, 40 "suppress mock implementation check, avoid import cycle if mocks generated outside of the tested package") 41 flag.BoolVar(&flags.remove, "rm", false, "first remove output file, if it exists") 42 flag.BoolVar(&flags.force, "force", false, "force generation, otherwise check if go generate file is newer than output file") 43 flag.BoolVar(&flags.verbose, "verbose", false, "verbose mode") 44 45 flag.Usage = func() { 46 fmt.Println(`moq [flags] source-dir interface [interface2 [interface3 [...]]]`) 47 flag.PrintDefaults() 48 fmt.Println(`Specifying an alias for the mock is also supported with the format 'interface:alias'`) 49 fmt.Println(`Ex: moq -pkg different . MyInterface:MyMock`) 50 } 51 52 flag.Parse() 53 flags.args = flag.Args() 54 55 if *printVersion { 56 fmt.Printf("moq version %s\n", Version) 57 os.Exit(0) 58 } 59 60 if err := run(flags); err != nil { 61 fmt.Fprintln(os.Stderr, err) 62 flag.Usage() 63 os.Exit(1) 64 } 65 } 66 67 func run(flags userFlags) error { 68 if len(flags.args) < 2 { 69 return errors.New("not enough arguments") 70 } 71 72 inFile := os.Getenv("GOFILE") 73 74 if flags.verbose { 75 if inFile == "" { 76 fmt.Fprintln(os.Stderr, "Mock in-file is unknown") 77 } else { 78 p, err := filepath.Abs(inFile) 79 if err != nil { 80 p = flags.outFile 81 } 82 fmt.Fprintln(os.Stderr, "Mock in-file is "+p) 83 } 84 85 if flags.outFile == "" { 86 fmt.Fprintln(os.Stderr, "Mock out-file is stdout") 87 } else { 88 p, err := filepath.Abs(flags.outFile) 89 if err != nil { 90 p = flags.outFile 91 } 92 fmt.Fprintln(os.Stderr, "Mock out-file is "+p) 93 } 94 } 95 96 if !flags.force { 97 if !needsRegeneration(inFile, flags.outFile) { 98 if flags.verbose { 99 fmt.Fprintln(os.Stderr, "Skipping mock generation as the input file hasn't changed since the mock was generated") 100 } 101 return nil 102 } 103 } 104 105 if flags.remove && flags.outFile != "" { 106 if err := os.Remove(flags.outFile); err != nil { 107 if !errors.Is(err, os.ErrNotExist) { 108 return err 109 } 110 } 111 } 112 113 var buf bytes.Buffer 114 var out io.Writer = os.Stdout 115 if flags.outFile != "" { 116 out = &buf 117 } 118 119 srcDir, args := flags.args[0], flags.args[1:] 120 m, err := moq.New(moq.Config{ 121 SrcDir: srcDir, 122 PkgName: flags.pkgName, 123 Formatter: flags.formatter, 124 StubImpl: flags.stubImpl, 125 SkipEnsure: flags.skipEnsure, 126 }) 127 if err != nil { 128 return err 129 } 130 131 if err = m.Mock(out, args...); err != nil { 132 return err 133 } 134 135 if flags.outFile == "" { 136 fmt.Fprintln(os.Stderr, "Mock written.") 137 return nil 138 } 139 140 // create the file 141 err = os.MkdirAll(filepath.Dir(flags.outFile), 0750) 142 if err != nil { 143 return err 144 } 145 146 err = ioutil.WriteFile(flags.outFile, buf.Bytes(), 0600) 147 148 if flags.verbose { 149 fmt.Fprintln(os.Stderr, "Mock written.") 150 } 151 152 return err 153 } 154 155 func needsRegeneration(inFile, outFile string) bool { 156 if outFile == "" { 157 // Assume that the user wants to print to stdout thus we have nothing to 158 // compare files and timestamps with. 159 return true 160 } 161 162 if inFile == "" { 163 // We were not called via go generate, so we have nothing to compare 164 // with. 165 return true 166 } 167 168 inInfo, err := os.Stat(inFile) 169 if err != nil { 170 if errors.Is(err, os.ErrNotExist) { 171 // Somehow the input file does not exist, which is weird as it's 172 // only provided by Go generate and there it should always exists, 173 // but let's assume it's run manually with wrong configuration. 174 return true 175 } 176 177 // Something went wrong stating the input file, so let's hope 178 // regeneration should be done and will work. 179 fmt.Fprintln(os.Stderr, err) 180 return true 181 } 182 183 outInfo, err := os.Stat(outFile) 184 if err != nil { 185 if errors.Is(err, os.ErrNotExist) { 186 // Likely the output file does not exist yet/anymore, so we should 187 // regenerate. 188 return true 189 } 190 191 // Something went wrong stating the output file, so let's hope 192 // regeneration should be done and will work. 193 fmt.Fprintln(os.Stderr, err) 194 return true 195 } 196 197 // The actual comparison. 198 return inInfo.ModTime().After(outInfo.ModTime()) 199 }