bindata/convert.go

165 lines
3.7 KiB
Go
Raw Normal View History

// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
// license. Its contents can be found at:
// http://creativecommons.org/publicdomain/zero/1.0/
package bindata
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"unicode"
)
// ProgressFunc is a callback handler which is fired whenever
// bindata begins translating a new file.
//
// It takes the file path and the current and total file counts.
// This can be used to indicate progress of the conversion.
//
// If this handler returns true, the processing is stopped and
// Translate() returns immediately.
type ProgressFunc func(file string, current, total int) bool
// Translate reads assets from an input directory, converts them
// to Go code and writes new files to the output directory specified
// in the given configuration.
func Translate(c *Config, pf ProgressFunc) error {
var toc []Asset
err := findFiles(c.Input, c.Prefix, &toc)
if err != nil {
return err
}
// Open output files.
debug, release, err := openOutput(c.Output)
if err != nil {
return err
}
defer func() {
debug.Close()
release.Close()
}()
// Prepare files -- write package header and build tags.
writeDebugHeader(debug, c)
writeReleaseHeader(release, c)
// Convert assets and write them to the output files.
var current int
for i := range toc {
if pf != nil {
current++
if pf(toc[i].Path, current, len(toc)) {
return nil
}
}
writeDebug(debug, c, &toc[i])
writeRelease(release, c, &toc[i])
}
// Generate TOC file.
return nil
}
// openOutput opens two output files. One for debug code and
// one for release code.
func openOutput(dir string) (*os.File, *os.File, error) {
debug, err := os.Create(filepath.Join(dir, "bindata_debug.go"))
if err != nil {
return nil, nil, err
}
release, err := os.Create(filepath.Join(dir, "bindata_release.go"))
if err != nil {
debug.Close()
return nil, nil, err
}
return debug, release, nil
}
// fillTOC recursively finds all the file paths in the given directory tree.
// They are added to the given map as keys. Values will be safe function names
// for each file, which will be used when generating the output code.
func findFiles(dir, prefix string, toc *[]Asset) error {
if len(prefix) > 0 {
dir, _ = filepath.Abs(dir)
prefix, _ = filepath.Abs(prefix)
}
fd, err := os.Open(dir)
if err != nil {
return err
}
defer fd.Close()
list, err := fd.Readdir(0)
if err != nil {
return err
}
for _, file := range list {
var asset Asset
asset.Path = filepath.Join(dir, file.Name())
asset.Name = asset.Path
if file.IsDir() {
findFiles(asset.Path, prefix, toc)
continue
}
if strings.HasPrefix(asset.Name, prefix) {
asset.Name = asset.Name[len(prefix):]
}
// If we have a leading slash, get rid of it.
if len(asset.Name) > 0 && asset.Name[0] == '/' {
asset.Name = asset.Name[1:]
}
// This shouldn't happen.
if len(asset.Name) == 0 {
return fmt.Errorf("Invalid file: %v", asset.Path)
}
asset.Func = safeFunctionName(asset.Name)
asset.Path, _ = filepath.Abs(asset.Path)
*toc = append(*toc, asset)
}
return nil
}
var regFuncName = regexp.MustCompile(`[^a-zA-Z0-9_]`)
// safeFunctionName converts the given name into a name
// which qualifies as a valid function identifier.
func safeFunctionName(name string) string {
name = strings.ToLower(name)
name = regFuncName.ReplaceAllString(name, "_")
// Get rid of "__" instances for niceness.
for strings.Index(name, "__") > -1 {
name = strings.Replace(name, "__", "_", -1)
}
// Leading underscores are silly (unless they prefix a digit (see below)).
for len(name) > 1 && name[0] == '_' {
name = name[1:]
}
// Identifier can't start with a digit.
if unicode.IsDigit(rune(name[0])) {
name = "_" + name
}
return name
}