github.com/oNaiPs/go-generate-fast@v0.3.0/src/plugins/go-bindata/main.go (about) 1 // from https://github.com/go-bindata/go-bindata/blob/master/go-bindata/main.go 2 package plugin_go_bindata 3 4 import ( 5 "flag" 6 "fmt" 7 "os" 8 "path/filepath" 9 "regexp" 10 "sort" 11 "strings" 12 "unicode" 13 14 "github.com/go-bindata/go-bindata" 15 ) 16 17 // parseArgs create s a new, filled configuration instance 18 // by reading and parsing command line options. 19 // 20 // This function exits the program with an error, if 21 // any of the command line options are incorrect. 22 func parseArgs(args []string) *bindata.Config { 23 flag := flag.NewFlagSet("Gobindata", flag.ContinueOnError) 24 25 var version bool 26 27 c := bindata.NewConfig() 28 29 flag.Usage = func() { 30 fmt.Printf("Usage: %s [options] <input directories>\n\n", os.Args[0]) 31 flag.PrintDefaults() 32 } 33 34 flag.BoolVar(&c.Debug, "debug", c.Debug, "Do not embed the assets, but provide the embedding API. Contents will still be loaded from disk.") 35 flag.BoolVar(&c.Dev, "dev", c.Dev, "Similar to debug, but does not emit absolute paths. Expects a rootDir variable to already exist in the generated code's package.") 36 flag.StringVar(&c.Tags, "tags", c.Tags, "Optional set of build tags to include.") 37 flag.StringVar(&c.Prefix, "prefix", c.Prefix, "Optional path prefix to strip off asset names.") 38 flag.StringVar(&c.Package, "pkg", c.Package, "Package name to use in the generated code.") 39 flag.BoolVar(&c.NoMemCopy, "nomemcopy", c.NoMemCopy, "Use a .rodata hack to get rid of unnecessary memcopies. Refer to the documentation to see what implications this carries.") 40 flag.BoolVar(&c.NoCompress, "nocompress", c.NoCompress, "Assets will *not* be GZIP compressed when this flag is specified.") 41 flag.BoolVar(&c.NoMetadata, "nometadata", c.NoMetadata, "Assets will not preserve size, mode, and modtime info.") 42 flag.BoolVar(&c.HttpFileSystem, "fs", c.HttpFileSystem, "Whether generate instance http.FileSystem interface code.") 43 flag.UintVar(&c.Mode, "mode", c.Mode, "Optional file mode override for all files.") 44 flag.Int64Var(&c.ModTime, "modtime", c.ModTime, "Optional modification unix timestamp override for all files.") 45 flag.StringVar(&c.Output, "o", c.Output, "Optional name of the output file to be generated.") 46 flag.BoolVar(&version, "version", false, "Displays version information.") 47 48 ignore := make([]string, 0) 49 flag.Var((*AppendSliceValue)(&ignore), "ignore", "Regex pattern to ignore") 50 51 err := flag.Parse(args) 52 if err != nil { 53 return nil 54 } 55 56 patterns := make([]*regexp.Regexp, 0) 57 for _, pattern := range ignore { 58 patterns = append(patterns, regexp.MustCompile(pattern)) 59 } 60 c.Ignore = patterns 61 62 if version { 63 return nil 64 } 65 66 // Make sure we have input paths. 67 if flag.NArg() == 0 { 68 return nil 69 } 70 71 // Create input configurations. 72 c.Input = make([]bindata.InputConfig, flag.NArg()) 73 for i := range c.Input { 74 c.Input[i] = parseInput(flag.Arg(i)) 75 } 76 77 return c 78 } 79 80 // parseRecursive determines whether the given path has a recursive indicator and 81 // returns a new path with the recursive indicator chopped off if it does. 82 // 83 // ex: 84 // /path/to/foo/... -> (/path/to/foo, true) 85 // /path/to/bar -> (/path/to/bar, false) 86 func parseInput(path string) bindata.InputConfig { 87 if strings.HasSuffix(path, "/...") { 88 return bindata.InputConfig{ 89 Path: filepath.Clean(path[:len(path)-4]), 90 Recursive: true, 91 } 92 } else { 93 return bindata.InputConfig{ 94 Path: filepath.Clean(path), 95 Recursive: false, 96 } 97 } 98 } 99 100 // ByName implements sort.Interface for []os.FileInfo based on Name() 101 type ByName []os.FileInfo 102 103 func (v ByName) Len() int { return len(v) } 104 func (v ByName) Swap(i, j int) { v[i], v[j] = v[j], v[i] } 105 func (v ByName) Less(i, j int) bool { return v[i].Name() < v[j].Name() } 106 107 // findFiles recursively finds all the file paths in the given directory tree. 108 // They are added to the given map as keys. Values will be safe function names 109 // for each file, which will be used when generating the output code. 110 func findFiles(dir, prefix string, recursive bool, toc *[]bindata.Asset, ignore []*regexp.Regexp, knownFuncs map[string]int, visitedPaths map[string]bool) error { 111 dirpath := dir 112 if len(prefix) > 0 { 113 dirpath, _ = filepath.Abs(dirpath) 114 prefix, _ = filepath.Abs(prefix) 115 prefix = filepath.ToSlash(prefix) 116 } 117 118 fi, err := os.Stat(dirpath) 119 if err != nil { 120 return err 121 } 122 123 var list []os.FileInfo 124 125 if !fi.IsDir() { 126 dirpath = filepath.Dir(dirpath) 127 list = []os.FileInfo{fi} 128 } else { 129 visitedPaths[dirpath] = true 130 fd, err := os.Open(dirpath) 131 if err != nil { 132 return err 133 } 134 135 defer fd.Close() 136 137 list, err = fd.Readdir(0) 138 if err != nil { 139 return err 140 } 141 142 // Sort to make output stable between invocations 143 sort.Sort(ByName(list)) 144 } 145 146 for _, file := range list { 147 var asset bindata.Asset 148 asset.Path = filepath.Join(dirpath, file.Name()) 149 asset.Name = filepath.ToSlash(asset.Path) 150 151 ignoring := false 152 for _, re := range ignore { 153 if re.MatchString(asset.Path) { 154 ignoring = true 155 break 156 } 157 } 158 if ignoring { 159 continue 160 } 161 162 if file.IsDir() { 163 if recursive { 164 recursivePath := filepath.Join(dir, file.Name()) 165 visitedPaths[asset.Path] = true 166 _ = findFiles(recursivePath, prefix, recursive, toc, ignore, knownFuncs, visitedPaths) 167 } 168 continue 169 } else if file.Mode()&os.ModeSymlink == os.ModeSymlink { 170 var linkPath string 171 if linkPath, err = os.Readlink(asset.Path); err != nil { 172 return err 173 } 174 if !filepath.IsAbs(linkPath) { 175 if linkPath, err = filepath.Abs(dirpath + "/" + linkPath); err != nil { 176 return err 177 } 178 } 179 if _, ok := visitedPaths[linkPath]; !ok { 180 visitedPaths[linkPath] = true 181 _ = findFiles(asset.Path, prefix, recursive, toc, ignore, knownFuncs, visitedPaths) 182 } 183 continue 184 } 185 186 if strings.HasPrefix(asset.Name, prefix) { 187 asset.Name = asset.Name[len(prefix):] 188 } else { 189 asset.Name = filepath.Join(dir, file.Name()) 190 } 191 192 // If we have a leading slash, get rid of it. 193 if len(asset.Name) > 0 && asset.Name[0] == '/' { 194 asset.Name = asset.Name[1:] 195 } 196 197 // This shouldn't happen. 198 if len(asset.Name) == 0 { 199 return fmt.Errorf("invalid file: %v", asset.Path) 200 } 201 202 asset.Func = safeFunctionName(asset.Name, knownFuncs) 203 asset.Path, _ = filepath.Abs(asset.Path) 204 *toc = append(*toc, asset) 205 } 206 207 return nil 208 } 209 210 var regFuncName = regexp.MustCompile(`[^a-zA-Z0-9_]`) 211 212 // safeFunctionName converts the given name into a name 213 // which qualifies as a valid function identifier. It 214 // also compares against a known list of functions to 215 // prevent conflict based on name translation. 216 func safeFunctionName(name string, knownFuncs map[string]int) string { 217 var inBytes, outBytes []byte 218 var toUpper bool 219 220 name = strings.ToLower(name) 221 inBytes = []byte(name) 222 223 for i := 0; i < len(inBytes); i++ { 224 if regFuncName.Match([]byte{inBytes[i]}) { 225 toUpper = true 226 } else if toUpper { 227 outBytes = append(outBytes, []byte(strings.ToUpper(string(inBytes[i])))...) 228 toUpper = false 229 } else { 230 outBytes = append(outBytes, inBytes[i]) 231 } 232 } 233 234 name = string(outBytes) 235 236 // Identifier can't start with a digit. 237 if unicode.IsDigit(rune(name[0])) { 238 name = "_" + name 239 } 240 241 if num, ok := knownFuncs[name]; ok { 242 knownFuncs[name] = num + 1 243 name = fmt.Sprintf("%s%d", name, num) 244 } else { 245 knownFuncs[name] = 2 246 } 247 248 return name 249 }