github.com/petergtz/pegomock@v2.9.1-0.20230424204322-eb0e044013df+incompatible/pegomock/watch/watch.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 watch
    16  
    17  import (
    18  	"fmt"
    19  	"io/ioutil"
    20  	"os"
    21  	"path/filepath"
    22  	"regexp"
    23  	"strings"
    24  
    25  	"gopkg.in/alecthomas/kingpin.v2"
    26  
    27  	"github.com/petergtz/pegomock/pegomock/filehandling"
    28  	"github.com/petergtz/pegomock/pegomock/util"
    29  )
    30  
    31  const wellKnownInterfaceListFile = "interfaces_to_mock"
    32  
    33  var join = strings.Join
    34  
    35  type MockFileUpdater struct {
    36  	recursive   bool
    37  	targetPaths []string
    38  	lastErrors  map[string]string
    39  }
    40  
    41  func NewMockFileUpdater(targetPaths []string, recursive bool) *MockFileUpdater {
    42  	return &MockFileUpdater{
    43  		targetPaths: targetPaths,
    44  		recursive:   recursive,
    45  		lastErrors:  make(map[string]string),
    46  	}
    47  }
    48  
    49  func (updater *MockFileUpdater) Update() {
    50  	for _, targetPath := range updater.targetPaths {
    51  		if updater.recursive {
    52  			filepath.Walk(targetPath, func(path string, info os.FileInfo, err error) error {
    53  				if err == nil && info.IsDir() {
    54  					util.WithinWorkingDir(path, updater.updateMockFiles)
    55  				}
    56  				return nil
    57  			})
    58  		} else {
    59  			util.WithinWorkingDir(targetPath, updater.updateMockFiles)
    60  		}
    61  	}
    62  }
    63  
    64  func (updater *MockFileUpdater) updateMockFiles(targetPath string) {
    65  	if _, err := os.Stat(wellKnownInterfaceListFile); os.IsNotExist(err) {
    66  		return
    67  	}
    68  	for _, lineParts := range linesIn(wellKnownInterfaceListFile) {
    69  		lineCmd := kingpin.New("What should go in here", "And what should go in here")
    70  		destination := lineCmd.Flag("output", "Output file; defaults to mock_<interface>_test.go.").Short('o').String()
    71  		nameOut := lineCmd.Flag("name", "Struct name of the generated code; defaults to the name of the interface prefixed with Mock").Default(filepath.Base(targetPath) + "_test").String()
    72  		packageOut := lineCmd.Flag("package", "Package of the generated code; defaults to the package from which pegomock was executed suffixed with _test").Default(filepath.Base(targetPath) + "_test").String()
    73  		selfPackage := lineCmd.Flag("self_package", "If set, the package this mock will be part of.").String()
    74  		lineArgs := lineCmd.Arg("args", "A (optional) Go package path + space-separated interface or a .go file").Required().Strings()
    75  
    76  		_, parseErr := lineCmd.Parse(lineParts)
    77  		if parseErr != nil {
    78  			fmt.Println("Error while trying to generate mock for line", join(lineParts, " "), ":", parseErr)
    79  			continue
    80  		}
    81  		defer func() {
    82  			err := recover()
    83  			if err != nil {
    84  				if updater.lastErrors[errorKey(*lineArgs)] != fmt.Sprint(err) {
    85  					fmt.Println("Error while trying to generate mock for", join(lineParts, " "), ":", err)
    86  					updater.lastErrors[errorKey(*lineArgs)] = fmt.Sprint(err)
    87  				}
    88  			}
    89  		}()
    90  
    91  		util.PanicOnError(util.ValidateArgs(*lineArgs))
    92  		sourceArgs, err := util.SourceArgs(*lineArgs)
    93  		util.PanicOnError(err)
    94  
    95  		generatedMockSourceCode, _ := filehandling.GenerateMockSourceCode(sourceArgs, *nameOut, *packageOut, *selfPackage, false, os.Stdout, false)
    96  		mockFilePath := filehandling.OutputFilePath(sourceArgs, ".", *destination)
    97  		hasChanged := util.WriteFileIfChanged(mockFilePath, generatedMockSourceCode)
    98  
    99  		if hasChanged || updater.lastErrors[errorKey(*lineArgs)] != "" {
   100  			fmt.Println("(Re)generated mock for", errorKey(*lineArgs), "in", mockFilePath)
   101  		}
   102  		delete(updater.lastErrors, errorKey(*lineArgs))
   103  	}
   104  }
   105  
   106  func errorKey(args []string) string {
   107  	return join(args, "_")
   108  }
   109  
   110  func CreateWellKnownInterfaceListFilesIfNecessary(targetPaths []string) {
   111  	for _, targetPath := range targetPaths {
   112  		CreateWellKnownInterfaceListFileIfNecessary(targetPath)
   113  	}
   114  }
   115  
   116  func CreateWellKnownInterfaceListFileIfNecessary(targetPath string) {
   117  	file, err := os.OpenFile(filepath.Join(targetPath, wellKnownInterfaceListFile), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
   118  	if err != nil {
   119  		if os.IsExist(err) {
   120  			return
   121  		}
   122  		panic(err)
   123  	}
   124  	defer file.Close()
   125  	file.WriteString("### List here all interfaces you would like to mock. One per line.\n")
   126  }
   127  
   128  func linesIn(file string) (result [][]string) {
   129  	content, err := ioutil.ReadFile(file)
   130  	util.PanicOnError(err)
   131  	for _, line := range strings.Split(string(content), "\n") {
   132  		if strings.HasPrefix(strings.TrimSpace(line), "#") || line == "" {
   133  			continue
   134  		}
   135  		parts := regexp.MustCompile(`\s`).Split(line, -1)
   136  		// TODO: do validation here like in main
   137  		result = append(result, parts)
   138  	}
   139  	return
   140  }