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 }