Use vfsgen instead of go-bindata (#7080)
* use vfsgen instead of go-bindata * fix templates * fix fmt * vendor vsfgentokarchuk/v1.17
parent
8eba27c792
commit
83b90e4199
@ -0,0 +1,23 @@ |
|||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"log" |
||||||
|
"net/http" |
||||||
|
|
||||||
|
"github.com/shurcooL/vfsgen" |
||||||
|
) |
||||||
|
|
||||||
|
func main() { |
||||||
|
var fsTemplates http.FileSystem = http.Dir("../../options") |
||||||
|
err := vfsgen.Generate(fsTemplates, vfsgen.Options{ |
||||||
|
PackageName: "options", |
||||||
|
BuildTags: "bindata", |
||||||
|
VariableName: "Assets", |
||||||
|
Filename: "bindata.go", |
||||||
|
}) |
||||||
|
if err != nil { |
||||||
|
log.Fatal("%v", err) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"log" |
||||||
|
"net/http" |
||||||
|
|
||||||
|
"github.com/shurcooL/vfsgen" |
||||||
|
) |
||||||
|
|
||||||
|
func main() { |
||||||
|
var fsPublic http.FileSystem = http.Dir("../../public") |
||||||
|
err := vfsgen.Generate(fsPublic, vfsgen.Options{ |
||||||
|
PackageName: "public", |
||||||
|
BuildTags: "bindata", |
||||||
|
VariableName: "Assets", |
||||||
|
Filename: "bindata.go", |
||||||
|
}) |
||||||
|
if err != nil { |
||||||
|
log.Fatal("%v", err) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"log" |
||||||
|
"net/http" |
||||||
|
|
||||||
|
"github.com/shurcooL/vfsgen" |
||||||
|
) |
||||||
|
|
||||||
|
func main() { |
||||||
|
var fsTemplates http.FileSystem = http.Dir("../../templates") |
||||||
|
err := vfsgen.Generate(fsTemplates, vfsgen.Options{ |
||||||
|
PackageName: "templates", |
||||||
|
BuildTags: "bindata", |
||||||
|
VariableName: "Assets", |
||||||
|
Filename: "bindata.go", |
||||||
|
}) |
||||||
|
if err != nil { |
||||||
|
log.Fatal("%v", err) |
||||||
|
} |
||||||
|
} |
@ -1,23 +0,0 @@ |
|||||||
Copyright (c) 2014, Elazar Leibovich |
|
||||||
All rights reserved. |
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without |
|
||||||
modification, are permitted provided that the following conditions are met: |
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this |
|
||||||
list of conditions and the following disclaimer. |
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice, |
|
||||||
this list of conditions and the following disclaimer in the documentation |
|
||||||
and/or other materials provided with the distribution. |
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
@ -1,46 +0,0 @@ |
|||||||
# go-bindata-assetfs |
|
||||||
|
|
||||||
Serve embedded files from [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) with `net/http`. |
|
||||||
|
|
||||||
[GoDoc](http://godoc.org/github.com/elazarl/go-bindata-assetfs) |
|
||||||
|
|
||||||
### Installation |
|
||||||
|
|
||||||
Install with |
|
||||||
|
|
||||||
$ go get github.com/jteeuwen/go-bindata/... |
|
||||||
$ go get github.com/elazarl/go-bindata-assetfs/... |
|
||||||
|
|
||||||
### Creating embedded data |
|
||||||
|
|
||||||
Usage is identical to [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) usage, |
|
||||||
instead of running `go-bindata` run `go-bindata-assetfs`. |
|
||||||
|
|
||||||
The tool will create a `bindata_assetfs.go` file, which contains the embedded data. |
|
||||||
|
|
||||||
A typical use case is |
|
||||||
|
|
||||||
$ go-bindata-assetfs data/... |
|
||||||
|
|
||||||
### Using assetFS in your code |
|
||||||
|
|
||||||
The generated file provides an `assetFS()` function that returns a `http.Filesystem` |
|
||||||
wrapping the embedded files. What you usually want to do is: |
|
||||||
|
|
||||||
http.Handle("/", http.FileServer(assetFS())) |
|
||||||
|
|
||||||
This would run an HTTP server serving the embedded files. |
|
||||||
|
|
||||||
## Without running binary tool |
|
||||||
|
|
||||||
You can always just run the `go-bindata` tool, and then |
|
||||||
|
|
||||||
use |
|
||||||
|
|
||||||
import "github.com/elazarl/go-bindata-assetfs" |
|
||||||
... |
|
||||||
http.Handle("/", |
|
||||||
http.FileServer( |
|
||||||
&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo, Prefix: "data"})) |
|
||||||
|
|
||||||
to serve files embedded from the `data` directory. |
|
@ -1,158 +0,0 @@ |
|||||||
package assetfs |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"errors" |
|
||||||
"io" |
|
||||||
"io/ioutil" |
|
||||||
"net/http" |
|
||||||
"os" |
|
||||||
"path" |
|
||||||
"path/filepath" |
|
||||||
"time" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
defaultFileTimestamp = time.Now() |
|
||||||
) |
|
||||||
|
|
||||||
// FakeFile implements os.FileInfo interface for a given path and size
|
|
||||||
type FakeFile struct { |
|
||||||
// Path is the path of this file
|
|
||||||
Path string |
|
||||||
// Dir marks of the path is a directory
|
|
||||||
Dir bool |
|
||||||
// Len is the length of the fake file, zero if it is a directory
|
|
||||||
Len int64 |
|
||||||
// Timestamp is the ModTime of this file
|
|
||||||
Timestamp time.Time |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FakeFile) Name() string { |
|
||||||
_, name := filepath.Split(f.Path) |
|
||||||
return name |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FakeFile) Mode() os.FileMode { |
|
||||||
mode := os.FileMode(0644) |
|
||||||
if f.Dir { |
|
||||||
return mode | os.ModeDir |
|
||||||
} |
|
||||||
return mode |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FakeFile) ModTime() time.Time { |
|
||||||
return f.Timestamp |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FakeFile) Size() int64 { |
|
||||||
return f.Len |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FakeFile) IsDir() bool { |
|
||||||
return f.Mode().IsDir() |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FakeFile) Sys() interface{} { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// AssetFile implements http.File interface for a no-directory file with content
|
|
||||||
type AssetFile struct { |
|
||||||
*bytes.Reader |
|
||||||
io.Closer |
|
||||||
FakeFile |
|
||||||
} |
|
||||||
|
|
||||||
func NewAssetFile(name string, content []byte, timestamp time.Time) *AssetFile { |
|
||||||
if timestamp.IsZero() { |
|
||||||
timestamp = defaultFileTimestamp |
|
||||||
} |
|
||||||
return &AssetFile{ |
|
||||||
bytes.NewReader(content), |
|
||||||
ioutil.NopCloser(nil), |
|
||||||
FakeFile{name, false, int64(len(content)), timestamp}} |
|
||||||
} |
|
||||||
|
|
||||||
func (f *AssetFile) Readdir(count int) ([]os.FileInfo, error) { |
|
||||||
return nil, errors.New("not a directory") |
|
||||||
} |
|
||||||
|
|
||||||
func (f *AssetFile) Size() int64 { |
|
||||||
return f.FakeFile.Size() |
|
||||||
} |
|
||||||
|
|
||||||
func (f *AssetFile) Stat() (os.FileInfo, error) { |
|
||||||
return f, nil |
|
||||||
} |
|
||||||
|
|
||||||
// AssetDirectory implements http.File interface for a directory
|
|
||||||
type AssetDirectory struct { |
|
||||||
AssetFile |
|
||||||
ChildrenRead int |
|
||||||
Children []os.FileInfo |
|
||||||
} |
|
||||||
|
|
||||||
func NewAssetDirectory(name string, children []string, fs *AssetFS) *AssetDirectory { |
|
||||||
fileinfos := make([]os.FileInfo, 0, len(children)) |
|
||||||
for _, child := range children { |
|
||||||
_, err := fs.AssetDir(filepath.Join(name, child)) |
|
||||||
fileinfos = append(fileinfos, &FakeFile{child, err == nil, 0, time.Time{}}) |
|
||||||
} |
|
||||||
return &AssetDirectory{ |
|
||||||
AssetFile{ |
|
||||||
bytes.NewReader(nil), |
|
||||||
ioutil.NopCloser(nil), |
|
||||||
FakeFile{name, true, 0, time.Time{}}, |
|
||||||
}, |
|
||||||
0, |
|
||||||
fileinfos} |
|
||||||
} |
|
||||||
|
|
||||||
func (f *AssetDirectory) Readdir(count int) ([]os.FileInfo, error) { |
|
||||||
if count <= 0 { |
|
||||||
return f.Children, nil |
|
||||||
} |
|
||||||
if f.ChildrenRead+count > len(f.Children) { |
|
||||||
count = len(f.Children) - f.ChildrenRead |
|
||||||
} |
|
||||||
rv := f.Children[f.ChildrenRead : f.ChildrenRead+count] |
|
||||||
f.ChildrenRead += count |
|
||||||
return rv, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (f *AssetDirectory) Stat() (os.FileInfo, error) { |
|
||||||
return f, nil |
|
||||||
} |
|
||||||
|
|
||||||
// AssetFS implements http.FileSystem, allowing
|
|
||||||
// embedded files to be served from net/http package.
|
|
||||||
type AssetFS struct { |
|
||||||
// Asset should return content of file in path if exists
|
|
||||||
Asset func(path string) ([]byte, error) |
|
||||||
// AssetDir should return list of files in the path
|
|
||||||
AssetDir func(path string) ([]string, error) |
|
||||||
// AssetInfo should return the info of file in path if exists
|
|
||||||
AssetInfo func(path string) (os.FileInfo, error) |
|
||||||
// Prefix would be prepended to http requests
|
|
||||||
Prefix string |
|
||||||
} |
|
||||||
|
|
||||||
func (fs *AssetFS) Open(name string) (http.File, error) { |
|
||||||
name = path.Join(fs.Prefix, name) |
|
||||||
if len(name) > 0 && name[0] == '/' { |
|
||||||
name = name[1:] |
|
||||||
} |
|
||||||
if b, err := fs.Asset(name); err == nil { |
|
||||||
timestamp := defaultFileTimestamp |
|
||||||
if info, err := fs.AssetInfo(name); err == nil { |
|
||||||
timestamp = info.ModTime() |
|
||||||
} |
|
||||||
return NewAssetFile(name, b, timestamp), nil |
|
||||||
} |
|
||||||
if children, err := fs.AssetDir(name); err == nil { |
|
||||||
return NewAssetDirectory(name, children, fs), nil |
|
||||||
} else { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
} |
|
@ -1,13 +0,0 @@ |
|||||||
// assetfs allows packages to serve static content embedded
|
|
||||||
// with the go-bindata tool with the standard net/http package.
|
|
||||||
//
|
|
||||||
// See https://github.com/jteeuwen/go-bindata for more information
|
|
||||||
// about embedding binary data with go-bindata.
|
|
||||||
//
|
|
||||||
// Usage example, after running
|
|
||||||
// $ go-bindata data/...
|
|
||||||
// use:
|
|
||||||
// http.Handle("/",
|
|
||||||
// http.FileServer(
|
|
||||||
// &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "data"}))
|
|
||||||
package assetfs |
|
@ -1,14 +0,0 @@ |
|||||||
sudo: false |
|
||||||
language: go |
|
||||||
|
|
||||||
go: |
|
||||||
- 1.3 |
|
||||||
- 1.4 |
|
||||||
- 1.5 |
|
||||||
- tip |
|
||||||
|
|
||||||
script: go test -v -cover -race |
|
||||||
|
|
||||||
notifications: |
|
||||||
email: |
|
||||||
- u@gogs.io |
|
@ -1,191 +0,0 @@ |
|||||||
Apache License |
|
||||||
Version 2.0, January 2004 |
|
||||||
http://www.apache.org/licenses/ |
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
|
||||||
|
|
||||||
1. Definitions. |
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction, and |
|
||||||
distribution as defined by Sections 1 through 9 of this document. |
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright |
|
||||||
owner that is granting the License. |
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all other entities |
|
||||||
that control, are controlled by, or are under common control with that entity. |
|
||||||
For the purposes of this definition, "control" means (i) the power, direct or |
|
||||||
indirect, to cause the direction or management of such entity, whether by |
|
||||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the |
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity. |
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity exercising |
|
||||||
permissions granted by this License. |
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications, including |
|
||||||
but not limited to software source code, documentation source, and configuration |
|
||||||
files. |
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical transformation or |
|
||||||
translation of a Source form, including but not limited to compiled object code, |
|
||||||
generated documentation, and conversions to other media types. |
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or Object form, made |
|
||||||
available under the License, as indicated by a copyright notice that is included |
|
||||||
in or attached to the work (an example is provided in the Appendix below). |
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object form, that |
|
||||||
is based on (or derived from) the Work and for which the editorial revisions, |
|
||||||
annotations, elaborations, or other modifications represent, as a whole, an |
|
||||||
original work of authorship. For the purposes of this License, Derivative Works |
|
||||||
shall not include works that remain separable from, or merely link (or bind by |
|
||||||
name) to the interfaces of, the Work and Derivative Works thereof. |
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including the original version |
|
||||||
of the Work and any modifications or additions to that Work or Derivative Works |
|
||||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work |
|
||||||
by the copyright owner or by an individual or Legal Entity authorized to submit |
|
||||||
on behalf of the copyright owner. For the purposes of this definition, |
|
||||||
"submitted" means any form of electronic, verbal, or written communication sent |
|
||||||
to the Licensor or its representatives, including but not limited to |
|
||||||
communication on electronic mailing lists, source code control systems, and |
|
||||||
issue tracking systems that are managed by, or on behalf of, the Licensor for |
|
||||||
the purpose of discussing and improving the Work, but excluding communication |
|
||||||
that is conspicuously marked or otherwise designated in writing by the copyright |
|
||||||
owner as "Not a Contribution." |
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf |
|
||||||
of whom a Contribution has been received by Licensor and subsequently |
|
||||||
incorporated within the Work. |
|
||||||
|
|
||||||
2. Grant of Copyright License. |
|
||||||
|
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby |
|
||||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, |
|
||||||
irrevocable copyright license to reproduce, prepare Derivative Works of, |
|
||||||
publicly display, publicly perform, sublicense, and distribute the Work and such |
|
||||||
Derivative Works in Source or Object form. |
|
||||||
|
|
||||||
3. Grant of Patent License. |
|
||||||
|
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby |
|
||||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, |
|
||||||
irrevocable (except as stated in this section) patent license to make, have |
|
||||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where |
|
||||||
such license applies only to those patent claims licensable by such Contributor |
|
||||||
that are necessarily infringed by their Contribution(s) alone or by combination |
|
||||||
of their Contribution(s) with the Work to which such Contribution(s) was |
|
||||||
submitted. If You institute patent litigation against any entity (including a |
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a |
|
||||||
Contribution incorporated within the Work constitutes direct or contributory |
|
||||||
patent infringement, then any patent licenses granted to You under this License |
|
||||||
for that Work shall terminate as of the date such litigation is filed. |
|
||||||
|
|
||||||
4. Redistribution. |
|
||||||
|
|
||||||
You may reproduce and distribute copies of the Work or Derivative Works thereof |
|
||||||
in any medium, with or without modifications, and in Source or Object form, |
|
||||||
provided that You meet the following conditions: |
|
||||||
|
|
||||||
You must give any other recipients of the Work or Derivative Works a copy of |
|
||||||
this License; and |
|
||||||
You must cause any modified files to carry prominent notices stating that You |
|
||||||
changed the files; and |
|
||||||
You must retain, in the Source form of any Derivative Works that You distribute, |
|
||||||
all copyright, patent, trademark, and attribution notices from the Source form |
|
||||||
of the Work, excluding those notices that do not pertain to any part of the |
|
||||||
Derivative Works; and |
|
||||||
If the Work includes a "NOTICE" text file as part of its distribution, then any |
|
||||||
Derivative Works that You distribute must include a readable copy of the |
|
||||||
attribution notices contained within such NOTICE file, excluding those notices |
|
||||||
that do not pertain to any part of the Derivative Works, in at least one of the |
|
||||||
following places: within a NOTICE text file distributed as part of the |
|
||||||
Derivative Works; within the Source form or documentation, if provided along |
|
||||||
with the Derivative Works; or, within a display generated by the Derivative |
|
||||||
Works, if and wherever such third-party notices normally appear. The contents of |
|
||||||
the NOTICE file are for informational purposes only and do not modify the |
|
||||||
License. You may add Your own attribution notices within Derivative Works that |
|
||||||
You distribute, alongside or as an addendum to the NOTICE text from the Work, |
|
||||||
provided that such additional attribution notices cannot be construed as |
|
||||||
modifying the License. |
|
||||||
You may add Your own copyright statement to Your modifications and may provide |
|
||||||
additional or different license terms and conditions for use, reproduction, or |
|
||||||
distribution of Your modifications, or for any such Derivative Works as a whole, |
|
||||||
provided Your use, reproduction, and distribution of the Work otherwise complies |
|
||||||
with the conditions stated in this License. |
|
||||||
|
|
||||||
5. Submission of Contributions. |
|
||||||
|
|
||||||
Unless You explicitly state otherwise, any Contribution intentionally submitted |
|
||||||
for inclusion in the Work by You to the Licensor shall be under the terms and |
|
||||||
conditions of this License, without any additional terms or conditions. |
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of |
|
||||||
any separate license agreement you may have executed with Licensor regarding |
|
||||||
such Contributions. |
|
||||||
|
|
||||||
6. Trademarks. |
|
||||||
|
|
||||||
This License does not grant permission to use the trade names, trademarks, |
|
||||||
service marks, or product names of the Licensor, except as required for |
|
||||||
reasonable and customary use in describing the origin of the Work and |
|
||||||
reproducing the content of the NOTICE file. |
|
||||||
|
|
||||||
7. Disclaimer of Warranty. |
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, Licensor provides the |
|
||||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, |
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, |
|
||||||
including, without limitation, any warranties or conditions of TITLE, |
|
||||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are |
|
||||||
solely responsible for determining the appropriateness of using or |
|
||||||
redistributing the Work and assume any risks associated with Your exercise of |
|
||||||
permissions under this License. |
|
||||||
|
|
||||||
8. Limitation of Liability. |
|
||||||
|
|
||||||
In no event and under no legal theory, whether in tort (including negligence), |
|
||||||
contract, or otherwise, unless required by applicable law (such as deliberate |
|
||||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be |
|
||||||
liable to You for damages, including any direct, indirect, special, incidental, |
|
||||||
or consequential damages of any character arising as a result of this License or |
|
||||||
out of the use or inability to use the Work (including but not limited to |
|
||||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or |
|
||||||
any and all other commercial damages or losses), even if such Contributor has |
|
||||||
been advised of the possibility of such damages. |
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. |
|
||||||
|
|
||||||
While redistributing the Work or Derivative Works thereof, You may choose to |
|
||||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or |
|
||||||
other liability obligations and/or rights consistent with this License. However, |
|
||||||
in accepting such obligations, You may act only on Your own behalf and on Your |
|
||||||
sole responsibility, not on behalf of any other Contributor, and only if You |
|
||||||
agree to indemnify, defend, and hold each Contributor harmless for any liability |
|
||||||
incurred by, or claims asserted against, such Contributor by reason of your |
|
||||||
accepting any such warranty or additional liability. |
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS |
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work |
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following boilerplate |
|
||||||
notice, with the fields enclosed by brackets "[]" replaced with your own |
|
||||||
identifying information. (Don't include the brackets!) The text should be |
|
||||||
enclosed in the appropriate comment syntax for the file format. We also |
|
||||||
recommend that a file or class name and description of purpose be included on |
|
||||||
the same "printed page" as the copyright notice for easier identification within |
|
||||||
third-party archives. |
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner] |
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
you may not use this file except in compliance with the License. |
|
||||||
You may obtain a copy of the License at |
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0 |
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software |
|
||||||
distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
See the License for the specific language governing permissions and |
|
||||||
limitations under the License. |
|
@ -1,16 +0,0 @@ |
|||||||
# bindata [![Build Status](https://travis-ci.org/go-macaron/bindata.svg?branch=master)](https://travis-ci.org/go-macaron/bindata) [![](http://gocover.io/_badge/github.com/go-macaron/bindata)](http://gocover.io/github.com/go-macaron/bindata) |
|
||||||
|
|
||||||
Package bindata is a helper module that allows to use in-memory static and template files for Macaron via [go-bindata](https://github.com/jteeuwen/go-bindata). |
|
||||||
|
|
||||||
### Installation |
|
||||||
|
|
||||||
go get github.com/go-macaron/bindata |
|
||||||
|
|
||||||
## Getting Help |
|
||||||
|
|
||||||
- [API Reference](https://gowalker.org/github.com/go-macaron/bindata) |
|
||||||
- [Documentation](http://go-macaron.com/docs/middlewares/bindata) |
|
||||||
|
|
||||||
## License |
|
||||||
|
|
||||||
This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text. |
|
@ -1,117 +0,0 @@ |
|||||||
// Copyright 2014 Dustin Webber
|
|
||||||
// Copyright 2015 The Macaron Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package bindata is a helper module that allows to use in-memory static and template files for Macaron.
|
|
||||||
package bindata |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"fmt" |
|
||||||
"io" |
|
||||||
"os" |
|
||||||
|
|
||||||
"github.com/elazarl/go-bindata-assetfs" |
|
||||||
"gopkg.in/macaron.v1" |
|
||||||
) |
|
||||||
|
|
||||||
const _VERSION = "0.1.1" |
|
||||||
|
|
||||||
func Version() string { |
|
||||||
return _VERSION |
|
||||||
} |
|
||||||
|
|
||||||
type ( |
|
||||||
templateFileSystem struct { |
|
||||||
files []macaron.TemplateFile |
|
||||||
} |
|
||||||
|
|
||||||
templateFile struct { |
|
||||||
name string |
|
||||||
data []byte |
|
||||||
ext string |
|
||||||
} |
|
||||||
|
|
||||||
Options struct { |
|
||||||
// Asset should return content of file in path if exists
|
|
||||||
Asset func(path string) ([]byte, error) |
|
||||||
// AssetDir should return list of files in the path
|
|
||||||
AssetDir func(path string) ([]string, error) |
|
||||||
// AssetInfo should return the info of file in path if exists
|
|
||||||
AssetInfo func(path string) (os.FileInfo, error) |
|
||||||
// AssetNames should return list of all asset names
|
|
||||||
AssetNames func() []string |
|
||||||
// Prefix would be prepended to http requests
|
|
||||||
Prefix string |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
func Static(opt Options) *assetfs.AssetFS { |
|
||||||
fs := &assetfs.AssetFS{ |
|
||||||
Asset: opt.Asset, |
|
||||||
AssetDir: opt.AssetDir, |
|
||||||
AssetInfo: opt.AssetInfo, |
|
||||||
Prefix: opt.Prefix, |
|
||||||
} |
|
||||||
|
|
||||||
return fs |
|
||||||
} |
|
||||||
|
|
||||||
func (templates templateFileSystem) ListFiles() []macaron.TemplateFile { |
|
||||||
return templates.files |
|
||||||
} |
|
||||||
|
|
||||||
func (templates templateFileSystem) Get(name string) (io.Reader, error) { |
|
||||||
for i := range templates.files { |
|
||||||
if templates.files[i].Name()+templates.files[i].Ext() == name { |
|
||||||
return bytes.NewReader(templates.files[i].Data()), nil |
|
||||||
} |
|
||||||
} |
|
||||||
return nil, fmt.Errorf("file '%s' not found", name) |
|
||||||
} |
|
||||||
|
|
||||||
func (f *templateFile) Name() string { |
|
||||||
return f.name |
|
||||||
} |
|
||||||
|
|
||||||
func (f *templateFile) Data() []byte { |
|
||||||
return f.data |
|
||||||
} |
|
||||||
|
|
||||||
func (f *templateFile) Ext() string { |
|
||||||
return f.ext |
|
||||||
} |
|
||||||
|
|
||||||
func Templates(opt Options) templateFileSystem { |
|
||||||
fs := templateFileSystem{} |
|
||||||
fs.files = make([]macaron.TemplateFile, 0, 10) |
|
||||||
|
|
||||||
list := opt.AssetNames() |
|
||||||
|
|
||||||
for _, key := range list { |
|
||||||
ext := macaron.GetExt(key) |
|
||||||
|
|
||||||
data, err := opt.Asset(key) |
|
||||||
|
|
||||||
if err != nil { |
|
||||||
continue |
|
||||||
} |
|
||||||
|
|
||||||
name := (key[0 : len(key)-len(ext)]) |
|
||||||
|
|
||||||
fs.files = append(fs.files, &templateFile{name, data, ext}) |
|
||||||
} |
|
||||||
|
|
||||||
return fs |
|
||||||
} |
|
@ -0,0 +1,21 @@ |
|||||||
|
MIT License |
||||||
|
|
||||||
|
Copyright (c) 2015 Dmitri Shuralyov |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -0,0 +1,21 @@ |
|||||||
|
package vfsutil |
||||||
|
|
||||||
|
import ( |
||||||
|
"net/http" |
||||||
|
"os" |
||||||
|
) |
||||||
|
|
||||||
|
// File implements http.FileSystem using the native file system restricted to a
|
||||||
|
// specific file served at root.
|
||||||
|
//
|
||||||
|
// While the FileSystem.Open method takes '/'-separated paths, a File's string
|
||||||
|
// value is a filename on the native file system, not a URL, so it is separated
|
||||||
|
// by filepath.Separator, which isn't necessarily '/'.
|
||||||
|
type File string |
||||||
|
|
||||||
|
func (f File) Open(name string) (http.File, error) { |
||||||
|
if name != "/" { |
||||||
|
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist} |
||||||
|
} |
||||||
|
return os.Open(string(f)) |
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
// Package vfsutil implements some I/O utility functions for http.FileSystem.
|
||||||
|
package vfsutil |
||||||
|
|
||||||
|
import ( |
||||||
|
"io/ioutil" |
||||||
|
"net/http" |
||||||
|
"os" |
||||||
|
) |
||||||
|
|
||||||
|
// ReadDir reads the contents of the directory associated with file and
|
||||||
|
// returns a slice of FileInfo values in directory order.
|
||||||
|
func ReadDir(fs http.FileSystem, name string) ([]os.FileInfo, error) { |
||||||
|
f, err := fs.Open(name) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
defer f.Close() |
||||||
|
return f.Readdir(0) |
||||||
|
} |
||||||
|
|
||||||
|
// Stat returns the FileInfo structure describing file.
|
||||||
|
func Stat(fs http.FileSystem, name string) (os.FileInfo, error) { |
||||||
|
f, err := fs.Open(name) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
defer f.Close() |
||||||
|
return f.Stat() |
||||||
|
} |
||||||
|
|
||||||
|
// ReadFile reads the file named by path from fs and returns the contents.
|
||||||
|
func ReadFile(fs http.FileSystem, path string) ([]byte, error) { |
||||||
|
rc, err := fs.Open(path) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
defer rc.Close() |
||||||
|
return ioutil.ReadAll(rc) |
||||||
|
} |
@ -0,0 +1,146 @@ |
|||||||
|
package vfsutil |
||||||
|
|
||||||
|
import ( |
||||||
|
"io" |
||||||
|
"net/http" |
||||||
|
"os" |
||||||
|
pathpkg "path" |
||||||
|
"path/filepath" |
||||||
|
"sort" |
||||||
|
) |
||||||
|
|
||||||
|
// Walk walks the filesystem rooted at root, calling walkFn for each file or
|
||||||
|
// directory in the filesystem, including root. All errors that arise visiting files
|
||||||
|
// and directories are filtered by walkFn. The files are walked in lexical
|
||||||
|
// order.
|
||||||
|
func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error { |
||||||
|
info, err := Stat(fs, root) |
||||||
|
if err != nil { |
||||||
|
return walkFn(root, nil, err) |
||||||
|
} |
||||||
|
return walk(fs, root, info, walkFn) |
||||||
|
} |
||||||
|
|
||||||
|
// readDirNames reads the directory named by dirname and returns
|
||||||
|
// a sorted list of directory entries.
|
||||||
|
func readDirNames(fs http.FileSystem, dirname string) ([]string, error) { |
||||||
|
fis, err := ReadDir(fs, dirname) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
names := make([]string, len(fis)) |
||||||
|
for i := range fis { |
||||||
|
names[i] = fis[i].Name() |
||||||
|
} |
||||||
|
sort.Strings(names) |
||||||
|
return names, nil |
||||||
|
} |
||||||
|
|
||||||
|
// walk recursively descends path, calling walkFn.
|
||||||
|
func walk(fs http.FileSystem, path string, info os.FileInfo, walkFn filepath.WalkFunc) error { |
||||||
|
err := walkFn(path, info, nil) |
||||||
|
if err != nil { |
||||||
|
if info.IsDir() && err == filepath.SkipDir { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if !info.IsDir() { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
names, err := readDirNames(fs, path) |
||||||
|
if err != nil { |
||||||
|
return walkFn(path, info, err) |
||||||
|
} |
||||||
|
|
||||||
|
for _, name := range names { |
||||||
|
filename := pathpkg.Join(path, name) |
||||||
|
fileInfo, err := Stat(fs, filename) |
||||||
|
if err != nil { |
||||||
|
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { |
||||||
|
return err |
||||||
|
} |
||||||
|
} else { |
||||||
|
err = walk(fs, filename, fileInfo, walkFn) |
||||||
|
if err != nil { |
||||||
|
if !fileInfo.IsDir() || err != filepath.SkipDir { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// WalkFilesFunc is the type of the function called for each file or directory visited by WalkFiles.
|
||||||
|
// It's like filepath.WalkFunc, except it provides an additional ReadSeeker parameter for file being visited.
|
||||||
|
type WalkFilesFunc func(path string, info os.FileInfo, rs io.ReadSeeker, err error) error |
||||||
|
|
||||||
|
// WalkFiles walks the filesystem rooted at root, calling walkFn for each file or
|
||||||
|
// directory in the filesystem, including root. In addition to FileInfo, it passes an
|
||||||
|
// ReadSeeker to walkFn for each file it visits.
|
||||||
|
func WalkFiles(fs http.FileSystem, root string, walkFn WalkFilesFunc) error { |
||||||
|
file, info, err := openStat(fs, root) |
||||||
|
if err != nil { |
||||||
|
return walkFn(root, nil, nil, err) |
||||||
|
} |
||||||
|
return walkFiles(fs, root, info, file, walkFn) |
||||||
|
} |
||||||
|
|
||||||
|
// walkFiles recursively descends path, calling walkFn.
|
||||||
|
// It closes the input file after it's done with it, so the caller shouldn't.
|
||||||
|
func walkFiles(fs http.FileSystem, path string, info os.FileInfo, file http.File, walkFn WalkFilesFunc) error { |
||||||
|
err := walkFn(path, info, file, nil) |
||||||
|
file.Close() |
||||||
|
if err != nil { |
||||||
|
if info.IsDir() && err == filepath.SkipDir { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if !info.IsDir() { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
names, err := readDirNames(fs, path) |
||||||
|
if err != nil { |
||||||
|
return walkFn(path, info, nil, err) |
||||||
|
} |
||||||
|
|
||||||
|
for _, name := range names { |
||||||
|
filename := pathpkg.Join(path, name) |
||||||
|
file, fileInfo, err := openStat(fs, filename) |
||||||
|
if err != nil { |
||||||
|
if err := walkFn(filename, nil, nil, err); err != nil && err != filepath.SkipDir { |
||||||
|
return err |
||||||
|
} |
||||||
|
} else { |
||||||
|
err = walkFiles(fs, filename, fileInfo, file, walkFn) |
||||||
|
// file is closed by walkFiles, so we don't need to close it here.
|
||||||
|
if err != nil { |
||||||
|
if !fileInfo.IsDir() || err != filepath.SkipDir { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// openStat performs Open and Stat and returns results, or first error encountered.
|
||||||
|
// The caller is responsible for closing the returned file when done.
|
||||||
|
func openStat(fs http.FileSystem, name string) (http.File, os.FileInfo, error) { |
||||||
|
f, err := fs.Open(name) |
||||||
|
if err != nil { |
||||||
|
return nil, nil, err |
||||||
|
} |
||||||
|
fi, err := f.Stat() |
||||||
|
if err != nil { |
||||||
|
f.Close() |
||||||
|
return nil, nil, err |
||||||
|
} |
||||||
|
return f, fi, nil |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
sudo: false |
||||||
|
language: go |
||||||
|
go: |
||||||
|
- 1.x |
||||||
|
- master |
||||||
|
matrix: |
||||||
|
allow_failures: |
||||||
|
- go: master |
||||||
|
fast_finish: true |
||||||
|
install: |
||||||
|
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). |
||||||
|
script: |
||||||
|
- go get -t -v ./... |
||||||
|
- diff -u <(echo -n) <(gofmt -d -s .) |
||||||
|
- go tool vet . |
||||||
|
- go test -v -race ./... |
@ -0,0 +1,10 @@ |
|||||||
|
Contributing |
||||||
|
============ |
||||||
|
|
||||||
|
vfsgen is open source, thanks for considering contributing! |
||||||
|
|
||||||
|
Please note that vfsgen aims to be simple and minimalistic, with as little to configure as possible. If you'd like to remove or simplify code (while having tests continue to pass), fix bugs, or improve code (e.g., add missing error checking, etc.), PRs and issues are welcome. |
||||||
|
|
||||||
|
However, if you'd like to add new functionality that increases complexity or scope, please make an issue and discuss your proposal first. I'm unlikely to accept such changes outright. It might be that your request is already a part of other similar packages, or it might fit in their scope better. See [Comparison and Alternatives](https://github.com/shurcooL/vfsgen/tree/README-alternatives-and-comparison-section#comparison) sections. |
||||||
|
|
||||||
|
Thank you! |
@ -0,0 +1,21 @@ |
|||||||
|
MIT License |
||||||
|
|
||||||
|
Copyright (c) 2015 Dmitri Shuralyov |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -0,0 +1,201 @@ |
|||||||
|
vfsgen |
||||||
|
====== |
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/shurcooL/vfsgen.svg?branch=master)](https://travis-ci.org/shurcooL/vfsgen) [![GoDoc](https://godoc.org/github.com/shurcooL/vfsgen?status.svg)](https://godoc.org/github.com/shurcooL/vfsgen) |
||||||
|
|
||||||
|
Package vfsgen takes an http.FileSystem (likely at `go generate` time) and |
||||||
|
generates Go code that statically implements the provided http.FileSystem. |
||||||
|
|
||||||
|
Features: |
||||||
|
|
||||||
|
- Efficient generated code without unneccessary overhead. |
||||||
|
|
||||||
|
- Uses gzip compression internally (selectively, only for files that compress well). |
||||||
|
|
||||||
|
- Enables direct access to internal gzip compressed bytes via an optional interface. |
||||||
|
|
||||||
|
- Outputs `gofmt`ed Go code. |
||||||
|
|
||||||
|
Installation |
||||||
|
------------ |
||||||
|
|
||||||
|
```bash |
||||||
|
go get -u github.com/shurcooL/vfsgen |
||||||
|
``` |
||||||
|
|
||||||
|
Usage |
||||||
|
----- |
||||||
|
|
||||||
|
Package `vfsgen` is a Go code generator library. It has a `Generate` function that takes an input filesystem (as a [`http.FileSystem`](https://godoc.org/net/http#FileSystem) type), and generates a Go code file that statically implements the contents of the input filesystem. |
||||||
|
|
||||||
|
For example, we can use [`http.Dir`](https://godoc.org/net/http#Dir) as a `http.FileSystem` implementation that uses the contents of the `/path/to/assets` directory: |
||||||
|
|
||||||
|
```Go |
||||||
|
var fs http.FileSystem = http.Dir("/path/to/assets") |
||||||
|
``` |
||||||
|
|
||||||
|
Now, when you execute the following code: |
||||||
|
|
||||||
|
```Go |
||||||
|
err := vfsgen.Generate(fs, vfsgen.Options{}) |
||||||
|
if err != nil { |
||||||
|
log.Fatalln(err) |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
An assets_vfsdata.go file will be generated in the current directory: |
||||||
|
|
||||||
|
```Go |
||||||
|
// Code generated by vfsgen; DO NOT EDIT. |
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import ... |
||||||
|
|
||||||
|
// assets statically implements the virtual filesystem provided to vfsgen.Generate. |
||||||
|
var assets http.FileSystem = ... |
||||||
|
``` |
||||||
|
|
||||||
|
Then, in your program, you can use `assets` as any other [`http.FileSystem`](https://godoc.org/net/http#FileSystem), for example: |
||||||
|
|
||||||
|
```Go |
||||||
|
file, err := assets.Open("/some/file.txt") |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
defer file.Close() |
||||||
|
``` |
||||||
|
|
||||||
|
```Go |
||||||
|
http.Handle("/assets/", http.FileServer(assets)) |
||||||
|
``` |
||||||
|
|
||||||
|
`vfsgen` can be more useful when combined with build tags and go generate directives. This is described below. |
||||||
|
|
||||||
|
### `go generate` Usage |
||||||
|
|
||||||
|
vfsgen is great to use with go generate directives. The code invoking `vfsgen.Generate` can go in an assets_generate.go file, which can then be invoked via "//go:generate go run assets_generate.go". The input virtual filesystem can read directly from disk, or it can be more involved. |
||||||
|
|
||||||
|
By using build tags, you can create a development mode where assets are loaded directly from disk via `http.Dir`, but then statically implemented for final releases. |
||||||
|
|
||||||
|
For example, suppose your source filesystem is defined in a package with import path "example.com/project/data" as: |
||||||
|
|
||||||
|
```Go |
||||||
|
// +build dev |
||||||
|
|
||||||
|
package data |
||||||
|
|
||||||
|
import "net/http" |
||||||
|
|
||||||
|
// Assets contains project assets. |
||||||
|
var Assets http.FileSystem = http.Dir("assets") |
||||||
|
``` |
||||||
|
|
||||||
|
When built with the "dev" build tag, accessing `data.Assets` will read from disk directly via `http.Dir`. |
||||||
|
|
||||||
|
A generate helper file assets_generate.go can be invoked via "//go:generate go run -tags=dev assets_generate.go" directive: |
||||||
|
|
||||||
|
```Go |
||||||
|
// +build ignore |
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"log" |
||||||
|
|
||||||
|
"example.com/project/data" |
||||||
|
"github.com/shurcooL/vfsgen" |
||||||
|
) |
||||||
|
|
||||||
|
func main() { |
||||||
|
err := vfsgen.Generate(data.Assets, vfsgen.Options{ |
||||||
|
PackageName: "data", |
||||||
|
BuildTags: "!dev", |
||||||
|
VariableName: "Assets", |
||||||
|
}) |
||||||
|
if err != nil { |
||||||
|
log.Fatalln(err) |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
Note that "dev" build tag is used to access the source filesystem, and the output file will contain "!dev" build tag. That way, the statically implemented version will be used during normal builds and `go get`, when custom builds tags are not specified. |
||||||
|
|
||||||
|
### `vfsgendev` Usage |
||||||
|
|
||||||
|
`vfsgendev` is a binary that can be used to replace the need for the assets_generate.go file. |
||||||
|
|
||||||
|
Make sure it's installed and available in your PATH. |
||||||
|
|
||||||
|
```bash |
||||||
|
go get -u github.com/shurcooL/vfsgen/cmd/vfsgendev |
||||||
|
``` |
||||||
|
|
||||||
|
Then the "//go:generate go run -tags=dev assets_generate.go" directive can be replaced with: |
||||||
|
|
||||||
|
``` |
||||||
|
//go:generate vfsgendev -source="example.com/project/data".Assets |
||||||
|
``` |
||||||
|
|
||||||
|
vfsgendev accesses the source variable using "dev" build tag, and generates an output file with "!dev" build tag. |
||||||
|
|
||||||
|
### Additional Embedded Information |
||||||
|
|
||||||
|
All compressed files implement [`httpgzip.GzipByter` interface](https://godoc.org/github.com/shurcooL/httpgzip#GzipByter) for efficient direct access to the internal compressed bytes: |
||||||
|
|
||||||
|
```Go |
||||||
|
// GzipByter is implemented by compressed files for |
||||||
|
// efficient direct access to the internal compressed bytes. |
||||||
|
type GzipByter interface { |
||||||
|
// GzipBytes returns gzip compressed contents of the file. |
||||||
|
GzipBytes() []byte |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
Files that have been determined to not be worth gzip compressing (their compressed size is larger than original) implement [`httpgzip.NotWorthGzipCompressing` interface](https://godoc.org/github.com/shurcooL/httpgzip#NotWorthGzipCompressing): |
||||||
|
|
||||||
|
```Go |
||||||
|
// NotWorthGzipCompressing is implemented by files that were determined |
||||||
|
// not to be worth gzip compressing (the file size did not decrease as a result). |
||||||
|
type NotWorthGzipCompressing interface { |
||||||
|
// NotWorthGzipCompressing is a noop. It's implemented in order to indicate |
||||||
|
// the file is not worth gzip compressing. |
||||||
|
NotWorthGzipCompressing() |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
Comparison |
||||||
|
---------- |
||||||
|
|
||||||
|
vfsgen aims to be conceptually simple to use. The [`http.FileSystem`](https://godoc.org/net/http#FileSystem) abstraction is central to vfsgen. It's used as both input for code generation, and as output in the generated code. |
||||||
|
|
||||||
|
That enables great flexibility through orthogonality, since helpers and wrappers can operate on `http.FileSystem` without knowing about vfsgen. If you want, you can perform pre-processing, minifying assets, merging folders, filtering out files and otherwise modifying input via generic `http.FileSystem` middleware. |
||||||
|
|
||||||
|
It avoids unneccessary overhead by merging what was previously done with two distinct packages into a single package. |
||||||
|
|
||||||
|
It strives to be the best in its class in terms of code quality and efficiency of generated code. However, if your use goals are different, there are other similar packages that may fit your needs better. |
||||||
|
|
||||||
|
### Alternatives |
||||||
|
|
||||||
|
- [`go-bindata`](https://github.com/jteeuwen/go-bindata) - Reads from disk, generates Go code that provides access to data via a [custom API](https://github.com/jteeuwen/go-bindata#accessing-an-asset). |
||||||
|
- [`go-bindata-assetfs`](https://github.com/elazarl/go-bindata-assetfs) - Takes output of go-bindata and provides a wrapper that implements `http.FileSystem` interface (the same as what vfsgen outputs directly). |
||||||
|
- [`becky`](https://github.com/tv42/becky) - Embeds assets as string literals in Go source. |
||||||
|
- [`statik`](https://github.com/rakyll/statik) - Embeds a directory of static files to be accessed via `http.FileSystem` interface (sounds very similar to vfsgen); implementation sourced from [camlistore](https://camlistore.org). |
||||||
|
- [`go.rice`](https://github.com/GeertJohan/go.rice) - Makes working with resources such as HTML, JS, CSS, images and templates very easy. |
||||||
|
- [`esc`](https://github.com/mjibson/esc) - Embeds files into Go programs and provides `http.FileSystem` interfaces to them. |
||||||
|
- [`staticfiles`](https://github.com/bouk/staticfiles) - Allows you to embed a directory of files into your Go binary. |
||||||
|
- [`togo`](https://github.com/flazz/togo) - Generates a Go source file with a `[]byte` var containing the given file's contents. |
||||||
|
- [`fileb0x`](https://github.com/UnnoTed/fileb0x) - Simple customizable tool to embed files in Go. |
||||||
|
- [`embedfiles`](https://github.com/leighmcculloch/embedfiles) - Simple tool for embedding files in Go code as a map. |
||||||
|
- [`packr`](https://github.com/gobuffalo/packr) - Simple solution for bundling static assets inside of Go binaries. |
||||||
|
- [`rsrc`](https://github.com/akavel/rsrc) - Tool for embedding .ico & manifest resources in Go programs for Windows. |
||||||
|
|
||||||
|
Attribution |
||||||
|
----------- |
||||||
|
|
||||||
|
This package was originally based on the excellent work by [@jteeuwen](https://github.com/jteeuwen) on [`go-bindata`](https://github.com/jteeuwen/go-bindata) and [@elazarl](https://github.com/elazarl) on [`go-bindata-assetfs`](https://github.com/elazarl/go-bindata-assetfs). |
||||||
|
|
||||||
|
License |
||||||
|
------- |
||||||
|
|
||||||
|
- [MIT License](LICENSE) |
@ -0,0 +1,45 @@ |
|||||||
|
package vfsgen |
||||||
|
|
||||||
|
import "io" |
||||||
|
|
||||||
|
// commentWriter writes a Go comment to the underlying io.Writer,
|
||||||
|
// using line comment form (//).
|
||||||
|
type commentWriter struct { |
||||||
|
W io.Writer |
||||||
|
wroteSlashes bool // Wrote "//" at the beginning of the current line.
|
||||||
|
} |
||||||
|
|
||||||
|
func (c *commentWriter) Write(p []byte) (int, error) { |
||||||
|
var n int |
||||||
|
for i, b := range p { |
||||||
|
if !c.wroteSlashes { |
||||||
|
s := "//" |
||||||
|
if b != '\n' { |
||||||
|
s = "// " |
||||||
|
} |
||||||
|
if _, err := io.WriteString(c.W, s); err != nil { |
||||||
|
return n, err |
||||||
|
} |
||||||
|
c.wroteSlashes = true |
||||||
|
} |
||||||
|
n0, err := c.W.Write(p[i : i+1]) |
||||||
|
n += n0 |
||||||
|
if err != nil { |
||||||
|
return n, err |
||||||
|
} |
||||||
|
if b == '\n' { |
||||||
|
c.wroteSlashes = false |
||||||
|
} |
||||||
|
} |
||||||
|
return len(p), nil |
||||||
|
} |
||||||
|
|
||||||
|
func (c *commentWriter) Close() error { |
||||||
|
if !c.wroteSlashes { |
||||||
|
if _, err := io.WriteString(c.W, "//"); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
c.wroteSlashes = true |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
/* |
||||||
|
Package vfsgen takes an http.FileSystem (likely at `go generate` time) and |
||||||
|
generates Go code that statically implements the provided http.FileSystem. |
||||||
|
|
||||||
|
Features: |
||||||
|
|
||||||
|
- Efficient generated code without unneccessary overhead. |
||||||
|
|
||||||
|
- Uses gzip compression internally (selectively, only for files that compress well). |
||||||
|
|
||||||
|
- Enables direct access to internal gzip compressed bytes via an optional interface. |
||||||
|
|
||||||
|
- Outputs `gofmt`ed Go code. |
||||||
|
*/ |
||||||
|
package vfsgen |
@ -0,0 +1,485 @@ |
|||||||
|
package vfsgen |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"compress/gzip" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"io/ioutil" |
||||||
|
"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 { |
||||||
|
// Consider all errors reading the input filesystem as fatal.
|
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
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}} |
||||||
|
`)) |
@ -0,0 +1,45 @@ |
|||||||
|
package vfsgen |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
// Options for vfsgen code generation.
|
||||||
|
type Options struct { |
||||||
|
// Filename of the generated Go code output (including extension).
|
||||||
|
// If left empty, it defaults to "{{toLower .VariableName}}_vfsdata.go".
|
||||||
|
Filename string |
||||||
|
|
||||||
|
// PackageName is the name of the package in the generated code.
|
||||||
|
// If left empty, it defaults to "main".
|
||||||
|
PackageName string |
||||||
|
|
||||||
|
// BuildTags are the optional build tags in the generated code.
|
||||||
|
// The build tags syntax is specified by the go tool.
|
||||||
|
BuildTags string |
||||||
|
|
||||||
|
// VariableName is the name of the http.FileSystem variable in the generated code.
|
||||||
|
// If left empty, it defaults to "assets".
|
||||||
|
VariableName string |
||||||
|
|
||||||
|
// VariableComment is the comment of the http.FileSystem variable in the generated code.
|
||||||
|
// If left empty, it defaults to "{{.VariableName}} statically implements the virtual filesystem provided to vfsgen.".
|
||||||
|
VariableComment string |
||||||
|
} |
||||||
|
|
||||||
|
// fillMissing sets default values for mandatory options that are left empty.
|
||||||
|
func (opt *Options) fillMissing() { |
||||||
|
if opt.PackageName == "" { |
||||||
|
opt.PackageName = "main" |
||||||
|
} |
||||||
|
if opt.VariableName == "" { |
||||||
|
opt.VariableName = "assets" |
||||||
|
} |
||||||
|
if opt.Filename == "" { |
||||||
|
opt.Filename = fmt.Sprintf("%s_vfsdata.go", strings.ToLower(opt.VariableName)) |
||||||
|
} |
||||||
|
if opt.VariableComment == "" { |
||||||
|
opt.VariableComment = fmt.Sprintf("%s statically implements the virtual filesystem provided to vfsgen.", opt.VariableName) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package vfsgen |
||||||
|
|
||||||
|
import ( |
||||||
|
"io" |
||||||
|
) |
||||||
|
|
||||||
|
// stringWriter writes given bytes to underlying io.Writer as a Go interpreted string literal value,
|
||||||
|
// not including double quotes. It tracks the total number of bytes written.
|
||||||
|
type stringWriter struct { |
||||||
|
io.Writer |
||||||
|
N int64 // Total bytes written.
|
||||||
|
} |
||||||
|
|
||||||
|
func (sw *stringWriter) Write(p []byte) (n int, err error) { |
||||||
|
const hex = "0123456789abcdef" |
||||||
|
buf := []byte{'\\', 'x', 0, 0} |
||||||
|
for _, b := range p { |
||||||
|
buf[2], buf[3] = hex[b/16], hex[b%16] |
||||||
|
_, err = sw.Writer.Write(buf) |
||||||
|
if err != nil { |
||||||
|
return n, err |
||||||
|
} |
||||||
|
n++ |
||||||
|
sw.N++ |
||||||
|
} |
||||||
|
return n, nil |
||||||
|
} |
Loading…
Reference in new issue