github.com/vugu/vugu@v0.3.5/cmd/vugufmt/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"strings"
    13  
    14  	"github.com/vugu/vugu/vugufmt"
    15  )
    16  
    17  var (
    18  	exitCode    = 0
    19  	list        = flag.Bool("l", false, "list files whose formatting differs from vugufmt's")
    20  	write       = flag.Bool("w", false, "write result to (source) file instead of stdout")
    21  	simplifyAST = flag.Bool("s", false, "simplify code")
    22  	imports     = flag.Bool("i", false, "run goimports instead of gofmt")
    23  	doDiff      = flag.Bool("d", false, "display diffs instead of rewriting files")
    24  )
    25  
    26  func main() {
    27  	vugufmtMain()
    28  	os.Exit(exitCode)
    29  }
    30  
    31  func vugufmtMain() {
    32  	// Handle input flags
    33  	flag.Usage = func() {
    34  		fmt.Fprintf(os.Stderr, "usage: vugufmt [flags] [path ...]\n")
    35  		flag.PrintDefaults()
    36  	}
    37  	flag.Parse()
    38  
    39  	// If no file paths given, we are reading from stdin.
    40  	if flag.NArg() == 0 {
    41  		if err := processFile("<standard input>", os.Stdin, os.Stdout); err != nil {
    42  			report(err)
    43  		}
    44  		return
    45  	}
    46  
    47  	// Otherwise, we need to read a bunch of files
    48  	for i := 0; i < flag.NArg(); i++ {
    49  		path := flag.Arg(i)
    50  		switch dir, err := os.Stat(path); {
    51  		case err != nil:
    52  			report(err)
    53  		case dir.IsDir():
    54  			walkDir(path)
    55  		default:
    56  			if err := processFile(path, nil, os.Stdout); err != nil {
    57  				report(err)
    58  			}
    59  		}
    60  	}
    61  }
    62  
    63  func walkDir(path string) {
    64  	filepath.Walk(path, visitFile)
    65  }
    66  
    67  func visitFile(path string, f os.FileInfo, err error) error {
    68  	if err == nil && isVuguFile(f) {
    69  		err = processFile(path, nil, os.Stdout)
    70  	}
    71  
    72  	// Don't complain if a file was deleted in the meantime (i.e.
    73  	// the directory changed concurrently while running gofmt).
    74  	if err != nil && !os.IsNotExist(err) {
    75  		report(err)
    76  	}
    77  	return nil
    78  }
    79  
    80  func isVuguFile(f os.FileInfo) bool {
    81  	// ignore non-Vugu files (except html)
    82  	name := f.Name()
    83  	return !f.IsDir() &&
    84  		!strings.HasPrefix(name, ".") &&
    85  		(strings.HasSuffix(name, ".vugu") || (strings.HasSuffix(name, ".html")))
    86  }
    87  
    88  func report(err error) {
    89  	fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(err.Error()))
    90  	exitCode = 2
    91  }
    92  
    93  func processFile(filename string, in io.Reader, out io.Writer) error {
    94  	var perm os.FileMode = 0644
    95  	// open the file if needed
    96  	if in == nil {
    97  		f, err := os.Open(filename)
    98  		if err != nil {
    99  			return err
   100  		}
   101  		defer f.Close()
   102  
   103  		fi, err := f.Stat()
   104  		if err != nil {
   105  			return err
   106  		}
   107  		in = f
   108  		perm = fi.Mode().Perm()
   109  	}
   110  
   111  	src, err := ioutil.ReadAll(in)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	var resBuff bytes.Buffer
   117  
   118  	var formatter *vugufmt.Formatter
   119  	if *imports {
   120  		formatter = vugufmt.NewFormatter(vugufmt.UseGoImports)
   121  	} else {
   122  		formatter = vugufmt.NewFormatter(vugufmt.UseGoFmt(*simplifyAST))
   123  	}
   124  
   125  	if !*list && !*doDiff {
   126  		if err := formatter.FormatHTML(filename, bytes.NewReader(src), &resBuff); err != nil {
   127  			return err
   128  		}
   129  		res := resBuff.Bytes()
   130  
   131  		if *write {
   132  			// make a temporary backup before overwriting original
   133  			bakname, err := backupFile(filename+".", src, perm)
   134  			if err != nil {
   135  				return err
   136  			}
   137  			err = ioutil.WriteFile(filename, res, perm)
   138  			if err != nil {
   139  				os.Rename(bakname, filename)
   140  				return err
   141  			}
   142  			err = os.Remove(bakname)
   143  			if err != nil {
   144  				return err
   145  			}
   146  		} else {
   147  			// just write to stdout
   148  			_, err = out.Write(res)
   149  		}
   150  	} else {
   151  		different, err := formatter.Diff(filename, bytes.NewReader(src), &resBuff)
   152  		if err != nil {
   153  			return fmt.Errorf("computing diff: %s", err)
   154  		}
   155  		if *list {
   156  			if different {
   157  				fmt.Fprintln(out, filename)
   158  			}
   159  		} else if *doDiff {
   160  			out.Write(resBuff.Bytes())
   161  		}
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  const chmodSupported = runtime.GOOS != "windows"
   168  
   169  // backupFile writes data to a new file named filename<number> with permissions perm,
   170  // with <number randomly chosen such that the file name is unique. backupFile returns
   171  // the chosen file name.
   172  func backupFile(filename string, data []byte, perm os.FileMode) (string, error) {
   173  
   174  	// create backup file
   175  	f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename))
   176  	if err != nil {
   177  		return "", err
   178  	}
   179  
   180  	bakname := f.Name()
   181  
   182  	if chmodSupported {
   183  		err = f.Chmod(perm)
   184  		if err != nil {
   185  			f.Close()
   186  			os.Remove(bakname)
   187  			return bakname, err
   188  		}
   189  	}
   190  
   191  	// write data to backup file
   192  	_, err = f.Write(data)
   193  	if err1 := f.Close(); err == nil {
   194  		err = err1
   195  	}
   196  	return bakname, err
   197  }