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  }