diff --git a/util/embed/main.go b/util/embed/main.go new file mode 100644 index 000000000..789d45632 --- /dev/null +++ b/util/embed/main.go @@ -0,0 +1,157 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + "text/template" +) + +var funcs = map[string]interface{}{ + "formatBytes": formatBytes, +} + +var ( + tmpl = template.Must(template.New("").Funcs(funcs).Parse(`// +build web + +// code is generated by embed. DO NOT EDIT. +package {{ .Package }} + +import ( + "bytes" + "errors" + "net/http" + "os" + "time" +) + +var assets = map[string][]byte{} + +var FS = &fs{} + +type fs struct {} + +func (fs *fs) Open(name string) (http.File, error) { + if name == "/" { + return fs, nil; + } + b, ok := assets[name] + if !ok { + return nil, os.ErrNotExist + } + return &file{name: name, size: len(b), Reader: bytes.NewReader(b)}, nil +} + +func (fs *fs) Close() error { return nil } +func (fs *fs) Read(p []byte) (int, error) { return 0, nil } +func (fs *fs) Seek(offset int64, whence int) (int64, error) { return 0, nil } +func (fs *fs) Stat() (os.FileInfo, error) { return fs, nil } +func (fs *fs) Name() string { return "/" } +func (fs *fs) Size() int64 { return 0 } +func (fs *fs) Mode() os.FileMode { return 0755} +func (fs *fs) ModTime() time.Time{ return time.Time{} } +func (fs *fs) IsDir() bool { return true } +func (fs *fs) Sys() interface{} { return nil } +func (fs *fs) Readdir(count int) ([]os.FileInfo, error) { + files := []os.FileInfo{} + for name, data := range assets { + files = append(files, &file{name: name, size: len(data), Reader: bytes.NewReader(data)}) + } + return files, nil +} + +type file struct { + name string + size int + *bytes.Reader +} + +func (f *file) Close() error { return nil } +func (f *file) Readdir(count int) ([]os.FileInfo, error) { return nil, errors.New("readdir is not supported") } +func (f *file) Stat() (os.FileInfo, error) { return f, nil } +func (f *file) Name() string { return f.name } +func (f *file) Size() int64 { return int64(f.size) } +func (f *file) Mode() os.FileMode { return 0644 } +func (f *file) ModTime() time.Time{ return time.Time{} } +func (f *file) IsDir() bool { return false } +func (f *file) Sys() interface{} { return nil } + +`))) + +// Embed is a helper function that embeds assets from the given directories +// into a Go source file. It is designed to be called from some generator +// script, see example project to find out how it can be used. +func Embed(packageName, file string, dirs ...string) error { + var buf bytes.Buffer + + // Execute template + if err := tmpl.Execute(&buf, struct{ + Package string + }{ + Package: packageName, + }); err != nil { + return err + } + + w, err := os.Create(file) + if err != nil { + return err + } + + defer w.Close() + + fmt.Fprintln(w, buf.String()) + fmt.Fprintln(w, `func init() {`) + + for _, dir := range dirs { + filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil + } + + log.Printf("packing %s...", path) + + b, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + path = filepath.ToSlash(path) + fmt.Fprintf(w, ` assets[%q] = []byte{`, strings.TrimPrefix(path, dir)) + fmt.Fprintf(w, formatBytes(b)) + fmt.Fprintln(w, `}`) + return nil + }) + } + + fmt.Fprintln(w, `}`) + + return nil +} + +func formatBytes(s []byte) string { + var builder strings.Builder + for _, v := range s { + builder.WriteString(fmt.Sprintf("0x%02x, ", int(v))) + } + return builder.String() +} + + +func main() { + var packageName string + var outputFile string + + flag.StringVar(&packageName,"package", "", "package name") + flag.StringVar(&outputFile,"output", "assets.go", "output filename") + flag.Parse() + args := flag.Args() + if err := Embed(packageName, outputFile, args...) ; err != nil { + log.Fatal(err) + } +}