Use static assets for HTML templates
[cacert-boardvoting.git] / boardvoting / main.go
1 package boardvoting
2
3 //go:generate go-bindata -pkg $GOPACKAGE -o assets.go ./migrations/... ./static/... ./templates/...
4
5 import (
6 "bytes"
7 "errors"
8 "io"
9 "io/ioutil"
10 "net/http"
11 "os"
12 "path/filepath"
13 "strings"
14 "time"
15 )
16
17 var defaultFileTimestamp = time.Now()
18
19 type AssetFS struct {
20 Asset func(path string) ([]byte, error)
21 AssetDir func(path string) ([]string, error)
22 AssetInfo func(path string) (os.FileInfo, error)
23 }
24
25 type FakeFile struct {
26 Path string
27 Dir bool
28 Len int64
29 Timestamp time.Time
30 }
31
32 func (f *FakeFile) Name() string {
33 _, name := filepath.Split(f.Path)
34 return name
35 }
36
37 func (f *FakeFile) Size() int64 { return f.Len }
38
39 func (f *FakeFile) Mode() os.FileMode {
40 mode := os.FileMode(0644)
41 if f.Dir {
42 return mode | os.ModeDir
43 }
44 return mode
45 }
46
47 func (f *FakeFile) ModTime() time.Time { return f.Timestamp }
48 func (f *FakeFile) IsDir() bool { return f.Dir }
49 func (f *FakeFile) Sys() interface{} { return nil }
50
51 type AssetFile struct {
52 *bytes.Reader
53 io.Closer
54 FakeFile
55 }
56
57 func NewAssetFile(name string, content []byte, timestamp time.Time) *AssetFile {
58 if timestamp.IsZero() {
59 timestamp = defaultFileTimestamp
60 }
61 return &AssetFile{
62 bytes.NewReader(content),
63 ioutil.NopCloser(nil),
64 FakeFile{name, false, int64(len(content)), timestamp},
65 }
66 }
67
68 func (f *AssetFile) Readdir(count int) ([]os.FileInfo, error) {
69 return nil, errors.New("not a directory")
70 }
71
72 func (f *AssetFile) Size() int64 {
73 return f.FakeFile.Size()
74 }
75
76 func (f *AssetFile) Stat() (os.FileInfo, error) {
77 return f, nil
78 }
79
80 type AssetDirectory struct {
81 AssetFile
82 ChildrenRead int
83 Children []os.FileInfo
84 }
85
86 func (f *AssetDirectory) Readdir(count int) ([]os.FileInfo, error) {
87 if count <= 0 {
88 return f.Children, nil
89 }
90 if f.ChildrenRead+count > len(f.Children) {
91 count = len(f.Children) - f.ChildrenRead
92 }
93 rv := f.Children[f.ChildrenRead : f.ChildrenRead+count]
94 f.ChildrenRead += count
95 return rv, nil
96 }
97
98 func (f *AssetDirectory) Stat() (os.FileInfo, error) {
99 return f, nil
100 }
101
102 func NewAssetDirectory(name string, children []string, fs *AssetFS) *AssetDirectory {
103 fileInfos := make([]os.FileInfo, 0, len(children))
104 for _, child := range children {
105 _, err := fs.AssetDir(filepath.Join(name, child))
106 fileInfos = append(fileInfos, &FakeFile{child, err == nil, 0, time.Time{}})
107 }
108 return &AssetDirectory{
109 AssetFile{
110 bytes.NewReader(nil),
111 ioutil.NopCloser(nil),
112 FakeFile{name, true, 0, time.Time{}},
113 },
114 0,
115 fileInfos}
116 }
117
118 func (f *AssetFS) Open(name string) (http.File, error) {
119 if len(name) > 0 && name[0] == '/' {
120 name = name[1:]
121 }
122 if b, err := f.Asset(name); err == nil {
123 timestamp := defaultFileTimestamp
124 if f.AssetInfo != nil {
125 if info, err := f.AssetInfo(name); err == nil {
126 timestamp = info.ModTime()
127 }
128 }
129 return NewAssetFile(name, b, timestamp), nil
130 }
131 if children, err := f.AssetDir(name); err == nil {
132 return NewAssetDirectory(name, children, f), nil
133 } else {
134 if strings.Contains(err.Error(), "not found") {
135 return nil, os.ErrNotExist
136 }
137 return nil, err
138 }
139 }
140
141 func GetAssetFS() *AssetFS {
142 return &AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo}
143 }