github.com/relnod/pegomock@v2.0.1+incompatible/pegomock/remove/remove.go (about)

     1  package remove
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"sort"
    11  	"strings"
    12  
    13  	"errors"
    14  )
    15  
    16  func Remove(
    17  	path string,
    18  	recursive bool,
    19  	shouldConfirm bool,
    20  	dryRun bool,
    21  	silent bool,
    22  	out io.Writer,
    23  	in io.Reader,
    24  	removeFn func(path string) error,
    25  ) {
    26  	filepaths, matchersDirPaths, e := getFilePaths(recursive, path, out)
    27  	if e != nil {
    28  		fmt.Fprintln(out, e.Error())
    29  		return
    30  	}
    31  	if len(filepaths) == 0 {
    32  		fmt.Fprintln(out, "No files to remove.")
    33  		return
    34  	}
    35  
    36  	allFilepathsSorted := make([]string, len(filepaths))
    37  	copy(allFilepathsSorted, filepaths)
    38  	for matchersDirPath := range matchersDirPaths {
    39  		if containsOnlyGeneratedFiles(matchersDirPath, allFilepathsSorted) {
    40  			allFilepathsSorted = append(allFilepathsSorted, matchersDirPath)
    41  		}
    42  	}
    43  	sort.Strings(allFilepathsSorted)
    44  
    45  	if dryRun {
    46  		fmt.Fprintln(out, "This is a dry-run. Would delete the following files:")
    47  		fmt.Fprintln(out, strings.Join(allFilepathsSorted, "\n"))
    48  		return
    49  	}
    50  
    51  	if shouldConfirm {
    52  		fmt.Fprintln(out, "Will delete the following files:")
    53  		fmt.Fprintln(out, strings.Join(allFilepathsSorted, "\n"))
    54  		if !askForConfirmation("Continue?", in, out) {
    55  			return
    56  		}
    57  	} else if !silent {
    58  		fmt.Fprintln(out, "Deleting the following files:")
    59  		fmt.Fprintln(out, strings.Join(allFilepathsSorted, "\n"))
    60  	}
    61  
    62  	var errs []error
    63  	for _, filepath := range filepaths {
    64  		e := removeFn(filepath)
    65  		if e != nil {
    66  			errs = append(errs, e)
    67  		}
    68  	}
    69  	for matcherPath := range matchersDirPaths {
    70  		if dirEmpty(matcherPath) {
    71  			e := removeFn(matcherPath)
    72  			if e != nil {
    73  				errs = append(errs, e)
    74  			}
    75  		}
    76  	}
    77  	if len(errs) > 0 {
    78  		fmt.Fprintf(out, "There were some errors when trying to delete files: %v", errs)
    79  	}
    80  }
    81  func containsOnlyGeneratedFiles(matchersDirPath string, generatedFilenames []string) bool {
    82  	f, e := os.Open(matchersDirPath)
    83  	if e != nil {
    84  		return false
    85  	}
    86  	defer f.Close()
    87  	filenamesInMatchersDir, e := f.Readdirnames(-1)
    88  	if e != nil {
    89  		return false
    90  	}
    91  	if len(difference(matchersDirPath, filenamesInMatchersDir, generatedFilenames)) == 0 {
    92  		return true
    93  	}
    94  	return false
    95  }
    96  
    97  func difference(root string, a, b []string) map[string]bool {
    98  	m := make(map[string]bool, len(b))
    99  	for _, s := range a {
   100  		m[filepath.Join(root, s)] = true
   101  	}
   102  	for _, s := range b {
   103  		delete(m, s)
   104  	}
   105  	return m
   106  }
   107  
   108  func dirEmpty(path string) bool {
   109  	f, e := os.Open(path)
   110  	if e != nil {
   111  		return false
   112  	}
   113  	defer f.Close()
   114  
   115  	_, e = f.Readdirnames(1)
   116  	return e == io.EOF
   117  }
   118  
   119  func getFilePaths(recursive bool, path string, out io.Writer) ([]string, map[string]bool, error) {
   120  	matcherPaths := make(map[string]bool)
   121  	var walk func(root string, walkFn filepath.WalkFunc) error
   122  	if recursive {
   123  		walk = filepath.Walk
   124  	} else {
   125  		walk = walkFilesInDir
   126  	}
   127  	filepaths := make([]string, 0)
   128  	e := walk(path, func(path string, info os.FileInfo, err error) error {
   129  		if !info.IsDir() && filepath.Ext(path) == ".go" && isPegomockGenerated(path, out) {
   130  			filepaths = append(filepaths, path)
   131  			if filepath.Base(filepath.Dir(path)) == "matchers" {
   132  				matcherPaths[filepath.Dir(path)] = true
   133  			}
   134  		}
   135  		return nil
   136  	})
   137  	if e != nil {
   138  		return nil, nil, errors.New("Could not get files in path " + path)
   139  	}
   140  	return filepaths, matcherPaths, nil
   141  }
   142  
   143  func walkFilesInDir(path string, walk filepath.WalkFunc) error {
   144  	fileInfos, e := ioutil.ReadDir(path)
   145  	if e != nil {
   146  		errors.New("Could not get files in path " + path)
   147  	}
   148  	for _, info := range fileInfos {
   149  		walk(filepath.Join(path, info.Name()), info, nil)
   150  	}
   151  	return nil
   152  }
   153  
   154  func isPegomockGenerated(path string, out io.Writer) bool {
   155  	file, e := os.Open(path)
   156  	if e != nil {
   157  		fmt.Fprintf(out, "Could not open file %v. Error: %v\n", path, e)
   158  		return false
   159  	}
   160  	b := make([]byte, 50)
   161  	_, e = file.Read(b)
   162  	if e != nil {
   163  		fmt.Fprintf(out, "Could not read from file %v. Error: %v\n", path, e)
   164  		return false
   165  	}
   166  	if strings.Contains(string(b), "// Code generated by pegomock. DO NOT EDIT.") {
   167  		return true
   168  	}
   169  	return false
   170  }
   171  
   172  func askForConfirmation(s string, in io.Reader, out io.Writer) bool {
   173  	reader := bufio.NewReader(in)
   174  
   175  	for {
   176  		fmt.Fprintf(out, "%s [y/n]: ", s)
   177  
   178  		response, err := reader.ReadString('\n')
   179  		if err != nil {
   180  			fmt.Fprintln(out, "Could not get confirmation from StdIn", err)
   181  			return false
   182  		}
   183  
   184  		response = strings.ToLower(strings.TrimSpace(response))
   185  
   186  		if response == "y" || response == "yes" {
   187  			return true
   188  		} else if response == "n" || response == "no" {
   189  			return false
   190  		}
   191  	}
   192  }