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) } }