bindata/convert.go

170 lines
3.5 KiB
Go

// 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 (
"bufio"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"unicode"
)
// Translate reads assets from an input directory, converts them
// to Go code and writes new files to the output specified
// in the given configuration.
func Translate(c *Config) error {
var toc []Asset
// Ensure our configuration has sane values.
err := c.validate()
if err != nil {
return err
}
// Locate all the assets.
for _, input := range c.Input {
err = findFiles(input.Path, c.Prefix, input.Recursive, &toc, c.Ignore)
if err != nil {
return err
}
}
// Create output file.
fd, err := os.Create(c.Output)
if err != nil {
return err
}
defer fd.Close()
// Create a buffered writer for better performance.
bfd := bufio.NewWriter(fd)
defer bfd.Flush()
// Write build tags, if applicable.
if len(c.Tags) > 0 {
_, err = fmt.Fprintf(bfd, "// +build %s\n\n", c.Tags)
if err != nil {
return err
}
}
// Write package declaration.
_, err = fmt.Fprintf(bfd, "package %s\n\n", c.Package)
if err != nil {
return err
}
// Write assets.
if c.Debug {
err = writeDebug(bfd, toc)
} else {
err = writeRelease(bfd, c, toc)
}
if err != nil {
return err
}
// Write table of contents
return writeTOC(bfd, toc)
}
// findFiles 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, recursive bool, toc *[]Asset, ignore []*regexp.Regexp) error {
if len(prefix) > 0 {
dir, _ = filepath.Abs(dir)
prefix, _ = filepath.Abs(prefix)
prefix = filepath.ToSlash(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 = filepath.ToSlash(asset.Path)
ignoring := false
for _, re := range ignore {
if re.MatchString(asset.Path) {
ignoring = true
break
}
}
if ignoring {
continue
}
if file.IsDir() {
if recursive {
findFiles(asset.Path, prefix, recursive, toc, ignore)
}
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
}