aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/shurcooL/vfsgen/generator.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/shurcooL/vfsgen/generator.go')
-rw-r--r--vendor/github.com/shurcooL/vfsgen/generator.go486
1 files changed, 486 insertions, 0 deletions
diff --git a/vendor/github.com/shurcooL/vfsgen/generator.go b/vendor/github.com/shurcooL/vfsgen/generator.go
new file mode 100644
index 00000000..a95c81cb
--- /dev/null
+++ b/vendor/github.com/shurcooL/vfsgen/generator.go
@@ -0,0 +1,486 @@
+package vfsgen
+
+import (
+ "bytes"
+ "compress/gzip"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ pathpkg "path"
+ "sort"
+ "strconv"
+ "text/template"
+ "time"
+
+ "github.com/shurcooL/httpfs/vfsutil"
+)
+
+// Generate Go code that statically implements input filesystem,
+// write the output to a file specified in opt.
+func Generate(input http.FileSystem, opt Options) error {
+ opt.fillMissing()
+
+ // Use an in-memory buffer to generate the entire output.
+ buf := new(bytes.Buffer)
+
+ err := t.ExecuteTemplate(buf, "Header", opt)
+ if err != nil {
+ return err
+ }
+
+ var toc toc
+ err = findAndWriteFiles(buf, input, &toc)
+ if err != nil {
+ return err
+ }
+
+ err = t.ExecuteTemplate(buf, "DirEntries", toc.dirs)
+ if err != nil {
+ return err
+ }
+
+ err = t.ExecuteTemplate(buf, "Trailer", toc)
+ if err != nil {
+ return err
+ }
+
+ // Write output file (all at once).
+ fmt.Println("writing", opt.Filename)
+ err = ioutil.WriteFile(opt.Filename, buf.Bytes(), 0644)
+ return err
+}
+
+type toc struct {
+ dirs []*dirInfo
+
+ HasCompressedFile bool // There's at least one compressedFile.
+ HasFile bool // There's at least one uncompressed file.
+}
+
+// fileInfo is a definition of a file.
+type fileInfo struct {
+ Path string
+ Name string
+ ModTime time.Time
+ UncompressedSize int64
+}
+
+// dirInfo is a definition of a directory.
+type dirInfo struct {
+ Path string
+ Name string
+ ModTime time.Time
+ Entries []string
+}
+
+// findAndWriteFiles 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 findAndWriteFiles(buf *bytes.Buffer, fs http.FileSystem, toc *toc) error {
+ walkFn := func(path string, fi os.FileInfo, r io.ReadSeeker, err error) error {
+ if err != nil {
+ log.Printf("can't stat file %q: %v\n", path, err)
+ return nil
+ }
+
+ switch fi.IsDir() {
+ case false:
+ file := &fileInfo{
+ Path: path,
+ Name: pathpkg.Base(path),
+ ModTime: fi.ModTime().UTC(),
+ UncompressedSize: fi.Size(),
+ }
+
+ marker := buf.Len()
+
+ // Write CompressedFileInfo.
+ err = writeCompressedFileInfo(buf, file, r)
+ switch err {
+ default:
+ return err
+ case nil:
+ toc.HasCompressedFile = true
+ // If compressed file is not smaller than original, revert and write original file.
+ case errCompressedNotSmaller:
+ _, err = r.Seek(0, io.SeekStart)
+ if err != nil {
+ return err
+ }
+
+ buf.Truncate(marker)
+
+ // Write FileInfo.
+ err = writeFileInfo(buf, file, r)
+ if err != nil {
+ return err
+ }
+ toc.HasFile = true
+ }
+ case true:
+ entries, err := readDirPaths(fs, path)
+ if err != nil {
+ return err
+ }
+
+ dir := &dirInfo{
+ Path: path,
+ Name: pathpkg.Base(path),
+ ModTime: fi.ModTime().UTC(),
+ Entries: entries,
+ }
+
+ toc.dirs = append(toc.dirs, dir)
+
+ // Write DirInfo.
+ err = t.ExecuteTemplate(buf, "DirInfo", dir)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+ }
+
+ err := vfsutil.WalkFiles(fs, "/", walkFn)
+ return err
+}
+
+// readDirPaths reads the directory named by dirname and returns
+// a sorted list of directory paths.
+func readDirPaths(fs http.FileSystem, dirname string) ([]string, error) {
+ fis, err := vfsutil.ReadDir(fs, dirname)
+ if err != nil {
+ return nil, err
+ }
+ paths := make([]string, len(fis))
+ for i := range fis {
+ paths[i] = pathpkg.Join(dirname, fis[i].Name())
+ }
+ sort.Strings(paths)
+ return paths, nil
+}
+
+// writeCompressedFileInfo writes CompressedFileInfo.
+// It returns errCompressedNotSmaller if compressed file is not smaller than original.
+func writeCompressedFileInfo(w io.Writer, file *fileInfo, r io.Reader) error {
+ err := t.ExecuteTemplate(w, "CompressedFileInfo-Before", file)
+ if err != nil {
+ return err
+ }
+ sw := &stringWriter{Writer: w}
+ gw := gzip.NewWriter(sw)
+ _, err = io.Copy(gw, r)
+ if err != nil {
+ return err
+ }
+ err = gw.Close()
+ if err != nil {
+ return err
+ }
+ if sw.N >= file.UncompressedSize {
+ return errCompressedNotSmaller
+ }
+ err = t.ExecuteTemplate(w, "CompressedFileInfo-After", file)
+ return err
+}
+
+var errCompressedNotSmaller = errors.New("compressed file is not smaller than original")
+
+// Write FileInfo.
+func writeFileInfo(w io.Writer, file *fileInfo, r io.Reader) error {
+ err := t.ExecuteTemplate(w, "FileInfo-Before", file)
+ if err != nil {
+ return err
+ }
+ sw := &stringWriter{Writer: w}
+ _, err = io.Copy(sw, r)
+ if err != nil {
+ return err
+ }
+ err = t.ExecuteTemplate(w, "FileInfo-After", file)
+ return err
+}
+
+var t = template.Must(template.New("").Funcs(template.FuncMap{
+ "quote": strconv.Quote,
+ "comment": func(s string) (string, error) {
+ var buf bytes.Buffer
+ cw := &commentWriter{W: &buf}
+ _, err := io.WriteString(cw, s)
+ if err != nil {
+ return "", err
+ }
+ err = cw.Close()
+ return buf.String(), err
+ },
+}).Parse(`{{define "Header"}}// Code generated by vfsgen; DO NOT EDIT.
+
+{{with .BuildTags}}// +build {{.}}
+
+{{end}}package {{.PackageName}}
+
+import (
+ "bytes"
+ "compress/gzip"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ pathpkg "path"
+ "time"
+)
+
+{{comment .VariableComment}}
+var {{.VariableName}} = func() http.FileSystem {
+ fs := vfsgen۰FS{
+{{end}}
+
+
+
+{{define "CompressedFileInfo-Before"}} {{quote .Path}}: &vfsgen۰CompressedFileInfo{
+ name: {{quote .Name}},
+ modTime: {{template "Time" .ModTime}},
+ uncompressedSize: {{.UncompressedSize}},
+{{/* This blank line separating compressedContent is neccessary to prevent potential gofmt issues. See issue #19. */}}
+ compressedContent: []byte("{{end}}{{define "CompressedFileInfo-After"}}"),
+ },
+{{end}}
+
+
+
+{{define "FileInfo-Before"}} {{quote .Path}}: &vfsgen۰FileInfo{
+ name: {{quote .Name}},
+ modTime: {{template "Time" .ModTime}},
+ content: []byte("{{end}}{{define "FileInfo-After"}}"),
+ },
+{{end}}
+
+
+
+{{define "DirInfo"}} {{quote .Path}}: &vfsgen۰DirInfo{
+ name: {{quote .Name}},
+ modTime: {{template "Time" .ModTime}},
+ },
+{{end}}
+
+
+
+{{define "DirEntries"}} }
+{{range .}}{{if .Entries}} fs[{{quote .Path}}].(*vfsgen۰DirInfo).entries = []os.FileInfo{{"{"}}{{range .Entries}}
+ fs[{{quote .}}].(os.FileInfo),{{end}}
+ }
+{{end}}{{end}}
+ return fs
+}()
+{{end}}
+
+
+
+{{define "Trailer"}}
+type vfsgen۰FS map[string]interface{}
+
+func (fs vfsgen۰FS) Open(path string) (http.File, error) {
+ path = pathpkg.Clean("/" + path)
+ f, ok := fs[path]
+ if !ok {
+ return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
+ }
+
+ switch f := f.(type) {{"{"}}{{if .HasCompressedFile}}
+ case *vfsgen۰CompressedFileInfo:
+ gr, err := gzip.NewReader(bytes.NewReader(f.compressedContent))
+ if err != nil {
+ // This should never happen because we generate the gzip bytes such that they are always valid.
+ panic("unexpected error reading own gzip compressed bytes: " + err.Error())
+ }
+ return &vfsgen۰CompressedFile{
+ vfsgen۰CompressedFileInfo: f,
+ gr: gr,
+ }, nil{{end}}{{if .HasFile}}
+ case *vfsgen۰FileInfo:
+ return &vfsgen۰File{
+ vfsgen۰FileInfo: f,
+ Reader: bytes.NewReader(f.content),
+ }, nil{{end}}
+ case *vfsgen۰DirInfo:
+ return &vfsgen۰Dir{
+ vfsgen۰DirInfo: f,
+ }, nil
+ default:
+ // This should never happen because we generate only the above types.
+ panic(fmt.Sprintf("unexpected type %T", f))
+ }
+}
+{{if .HasCompressedFile}}
+// vfsgen۰CompressedFileInfo is a static definition of a gzip compressed file.
+type vfsgen۰CompressedFileInfo struct {
+ name string
+ modTime time.Time
+ compressedContent []byte
+ uncompressedSize int64
+}
+
+func (f *vfsgen۰CompressedFileInfo) Readdir(count int) ([]os.FileInfo, error) {
+ return nil, fmt.Errorf("cannot Readdir from file %s", f.name)
+}
+func (f *vfsgen۰CompressedFileInfo) Stat() (os.FileInfo, error) { return f, nil }
+
+func (f *vfsgen۰CompressedFileInfo) GzipBytes() []byte {
+ return f.compressedContent
+}
+
+func (f *vfsgen۰CompressedFileInfo) Name() string { return f.name }
+func (f *vfsgen۰CompressedFileInfo) Size() int64 { return f.uncompressedSize }
+func (f *vfsgen۰CompressedFileInfo) Mode() os.FileMode { return 0444 }
+func (f *vfsgen۰CompressedFileInfo) ModTime() time.Time { return f.modTime }
+func (f *vfsgen۰CompressedFileInfo) IsDir() bool { return false }
+func (f *vfsgen۰CompressedFileInfo) Sys() interface{} { return nil }
+
+// vfsgen۰CompressedFile is an opened compressedFile instance.
+type vfsgen۰CompressedFile struct {
+ *vfsgen۰CompressedFileInfo
+ gr *gzip.Reader
+ grPos int64 // Actual gr uncompressed position.
+ seekPos int64 // Seek uncompressed position.
+}
+
+func (f *vfsgen۰CompressedFile) Read(p []byte) (n int, err error) {
+ if f.grPos > f.seekPos {
+ // Rewind to beginning.
+ err = f.gr.Reset(bytes.NewReader(f.compressedContent))
+ if err != nil {
+ return 0, err
+ }
+ f.grPos = 0
+ }
+ if f.grPos < f.seekPos {
+ // Fast-forward.
+ _, err = io.CopyN(ioutil.Discard, f.gr, f.seekPos-f.grPos)
+ if err != nil {
+ return 0, err
+ }
+ f.grPos = f.seekPos
+ }
+ n, err = f.gr.Read(p)
+ f.grPos += int64(n)
+ f.seekPos = f.grPos
+ return n, err
+}
+func (f *vfsgen۰CompressedFile) Seek(offset int64, whence int) (int64, error) {
+ switch whence {
+ case io.SeekStart:
+ f.seekPos = 0 + offset
+ case io.SeekCurrent:
+ f.seekPos += offset
+ case io.SeekEnd:
+ f.seekPos = f.uncompressedSize + offset
+ default:
+ panic(fmt.Errorf("invalid whence value: %v", whence))
+ }
+ return f.seekPos, nil
+}
+func (f *vfsgen۰CompressedFile) Close() error {
+ return f.gr.Close()
+}
+{{else}}
+// We already imported "compress/gzip" and "io/ioutil", but ended up not using them. Avoid unused import error.
+var _ = gzip.Reader{}
+var _ = ioutil.Discard
+{{end}}{{if .HasFile}}
+// vfsgen۰FileInfo is a static definition of an uncompressed file (because it's not worth gzip compressing).
+type vfsgen۰FileInfo struct {
+ name string
+ modTime time.Time
+ content []byte
+}
+
+func (f *vfsgen۰FileInfo) Readdir(count int) ([]os.FileInfo, error) {
+ return nil, fmt.Errorf("cannot Readdir from file %s", f.name)
+}
+func (f *vfsgen۰FileInfo) Stat() (os.FileInfo, error) { return f, nil }
+
+func (f *vfsgen۰FileInfo) NotWorthGzipCompressing() {}
+
+func (f *vfsgen۰FileInfo) Name() string { return f.name }
+func (f *vfsgen۰FileInfo) Size() int64 { return int64(len(f.content)) }
+func (f *vfsgen۰FileInfo) Mode() os.FileMode { return 0444 }
+func (f *vfsgen۰FileInfo) ModTime() time.Time { return f.modTime }
+func (f *vfsgen۰FileInfo) IsDir() bool { return false }
+func (f *vfsgen۰FileInfo) Sys() interface{} { return nil }
+
+// vfsgen۰File is an opened file instance.
+type vfsgen۰File struct {
+ *vfsgen۰FileInfo
+ *bytes.Reader
+}
+
+func (f *vfsgen۰File) Close() error {
+ return nil
+}
+{{else if not .HasCompressedFile}}
+// We already imported "bytes", but ended up not using it. Avoid unused import error.
+var _ = bytes.Reader{}
+{{end}}
+// vfsgen۰DirInfo is a static definition of a directory.
+type vfsgen۰DirInfo struct {
+ name string
+ modTime time.Time
+ entries []os.FileInfo
+}
+
+func (d *vfsgen۰DirInfo) Read([]byte) (int, error) {
+ return 0, fmt.Errorf("cannot Read from directory %s", d.name)
+}
+func (d *vfsgen۰DirInfo) Close() error { return nil }
+func (d *vfsgen۰DirInfo) Stat() (os.FileInfo, error) { return d, nil }
+
+func (d *vfsgen۰DirInfo) Name() string { return d.name }
+func (d *vfsgen۰DirInfo) Size() int64 { return 0 }
+func (d *vfsgen۰DirInfo) Mode() os.FileMode { return 0755 | os.ModeDir }
+func (d *vfsgen۰DirInfo) ModTime() time.Time { return d.modTime }
+func (d *vfsgen۰DirInfo) IsDir() bool { return true }
+func (d *vfsgen۰DirInfo) Sys() interface{} { return nil }
+
+// vfsgen۰Dir is an opened dir instance.
+type vfsgen۰Dir struct {
+ *vfsgen۰DirInfo
+ pos int // Position within entries for Seek and Readdir.
+}
+
+func (d *vfsgen۰Dir) Seek(offset int64, whence int) (int64, error) {
+ if offset == 0 && whence == io.SeekStart {
+ d.pos = 0
+ return 0, nil
+ }
+ return 0, fmt.Errorf("unsupported Seek in directory %s", d.name)
+}
+
+func (d *vfsgen۰Dir) Readdir(count int) ([]os.FileInfo, error) {
+ if d.pos >= len(d.entries) && count > 0 {
+ return nil, io.EOF
+ }
+ if count <= 0 || count > len(d.entries)-d.pos {
+ count = len(d.entries) - d.pos
+ }
+ e := d.entries[d.pos : d.pos+count]
+ d.pos += count
+ return e, nil
+}
+{{end}}
+
+
+
+{{define "Time"}}
+{{- if .IsZero -}}
+ time.Time{}
+{{- else -}}
+ time.Date({{.Year}}, {{printf "%d" .Month}}, {{.Day}}, {{.Hour}}, {{.Minute}}, {{.Second}}, {{.Nanosecond}}, time.UTC)
+{{- end -}}
+{{end}}
+`))