Skip to content

Commit 855d56b

Browse files
authored
Merge pull request #111 from go-git/embed
embedfs: Move the embedfs from go-git-fixtures
2 parents bbceac1 + 096d70e commit 855d56b

File tree

4 files changed

+599
-0
lines changed

4 files changed

+599
-0
lines changed

embedfs/embed.go

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
// embedfs exposes an embed.FS as a read-only billy.Filesystem.
2+
package embedfs
3+
4+
import (
5+
"bytes"
6+
"embed"
7+
"fmt"
8+
"io/fs"
9+
"os"
10+
"path/filepath"
11+
"sort"
12+
"strings"
13+
"sync"
14+
15+
"github.com/go-git/go-billy/v6"
16+
"github.com/go-git/go-billy/v6/memfs"
17+
)
18+
19+
type Embed struct {
20+
underlying *embed.FS
21+
}
22+
23+
func New(efs *embed.FS) billy.Filesystem {
24+
fs := &Embed{
25+
underlying: efs,
26+
}
27+
28+
if efs == nil {
29+
fs.underlying = &embed.FS{}
30+
}
31+
32+
return fs
33+
}
34+
35+
func (fs *Embed) Root() string {
36+
return ""
37+
}
38+
39+
func (fs *Embed) Stat(filename string) (os.FileInfo, error) {
40+
f, err := fs.underlying.Open(filename)
41+
if err != nil {
42+
return nil, err
43+
}
44+
return f.Stat()
45+
}
46+
47+
func (fs *Embed) Open(filename string) (billy.File, error) {
48+
return fs.OpenFile(filename, os.O_RDONLY, 0)
49+
}
50+
51+
func (fs *Embed) OpenFile(filename string, flag int, _ os.FileMode) (billy.File, error) {
52+
if flag&(os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_RDWR|os.O_EXCL|os.O_TRUNC) != 0 {
53+
return nil, billy.ErrReadOnly
54+
}
55+
56+
f, err := fs.underlying.Open(filename)
57+
if err != nil {
58+
return nil, err
59+
}
60+
61+
fi, err := f.Stat()
62+
if err != nil {
63+
return nil, err
64+
}
65+
66+
if fi.IsDir() {
67+
return nil, fmt.Errorf("cannot open directory: %s", filename)
68+
}
69+
70+
data, err := fs.underlying.ReadFile(filename)
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
// Only load the bytes to memory if the files is needed.
76+
lazyFunc := func() *bytes.Reader { return bytes.NewReader(data) }
77+
return toFile(lazyFunc, fi), nil
78+
}
79+
80+
// Join return a path with all elements joined by forward slashes.
81+
//
82+
// This behaviour is OS-agnostic.
83+
func (fs *Embed) Join(elem ...string) string {
84+
for i, el := range elem {
85+
if el != "" {
86+
clean := filepath.Clean(strings.Join(elem[i:], "/"))
87+
return filepath.ToSlash(clean)
88+
}
89+
}
90+
return ""
91+
}
92+
93+
func (fs *Embed) ReadDir(path string) ([]os.FileInfo, error) {
94+
e, err := fs.underlying.ReadDir(path)
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
entries := make([]os.FileInfo, 0, len(e))
100+
for _, f := range e {
101+
fi, _ := f.Info()
102+
entries = append(entries, fi)
103+
}
104+
105+
sort.Sort(memfs.ByName(entries))
106+
107+
return entries, nil
108+
}
109+
110+
// Chroot is not supported.
111+
//
112+
// Calls will always return billy.ErrNotSupported.
113+
func (fs *Embed) Chroot(_ string) (billy.Filesystem, error) {
114+
return nil, billy.ErrNotSupported
115+
}
116+
117+
// Lstat is not supported.
118+
//
119+
// Calls will always return billy.ErrNotSupported.
120+
func (fs *Embed) Lstat(_ string) (os.FileInfo, error) {
121+
return nil, billy.ErrNotSupported
122+
}
123+
124+
// Readlink is not supported.
125+
//
126+
// Calls will always return billy.ErrNotSupported.
127+
func (fs *Embed) Readlink(_ string) (string, error) {
128+
return "", billy.ErrNotSupported
129+
}
130+
131+
// TempFile is not supported.
132+
//
133+
// Calls will always return billy.ErrNotSupported.
134+
func (fs *Embed) TempFile(_, _ string) (billy.File, error) {
135+
return nil, billy.ErrNotSupported
136+
}
137+
138+
// Symlink is not supported.
139+
//
140+
// Calls will always return billy.ErrReadOnly.
141+
func (fs *Embed) Symlink(_, _ string) error {
142+
return billy.ErrReadOnly
143+
}
144+
145+
// Create is not supported.
146+
//
147+
// Calls will always return billy.ErrReadOnly.
148+
func (fs *Embed) Create(_ string) (billy.File, error) {
149+
return nil, billy.ErrReadOnly
150+
}
151+
152+
// Rename is not supported.
153+
//
154+
// Calls will always return billy.ErrReadOnly.
155+
func (fs *Embed) Rename(_, _ string) error {
156+
return billy.ErrReadOnly
157+
}
158+
159+
// Remove is not supported.
160+
//
161+
// Calls will always return billy.ErrReadOnly.
162+
func (fs *Embed) Remove(_ string) error {
163+
return billy.ErrReadOnly
164+
}
165+
166+
// MkdirAll is not supported.
167+
//
168+
// Calls will always return billy.ErrReadOnly.
169+
func (fs *Embed) MkdirAll(_ string, _ os.FileMode) error {
170+
return billy.ErrReadOnly
171+
}
172+
173+
func toFile(lazy func() *bytes.Reader, fi fs.FileInfo) billy.File {
174+
return &file{
175+
lazy: lazy,
176+
fi: fi,
177+
}
178+
}
179+
180+
type file struct {
181+
lazy func() *bytes.Reader
182+
reader *bytes.Reader
183+
fi fs.FileInfo
184+
once sync.Once
185+
}
186+
187+
func (f *file) loadReader() {
188+
f.reader = f.lazy()
189+
}
190+
191+
func (f *file) Name() string {
192+
return f.fi.Name()
193+
}
194+
195+
func (f *file) Read(b []byte) (int, error) {
196+
f.once.Do(f.loadReader)
197+
198+
return f.reader.Read(b)
199+
}
200+
201+
func (f *file) ReadAt(b []byte, off int64) (int, error) {
202+
f.once.Do(f.loadReader)
203+
204+
return f.reader.ReadAt(b, off)
205+
}
206+
207+
func (f *file) Seek(offset int64, whence int) (int64, error) {
208+
f.once.Do(f.loadReader)
209+
210+
return f.reader.Seek(offset, whence)
211+
}
212+
213+
func (f *file) Stat() (os.FileInfo, error) {
214+
return f.fi, nil
215+
}
216+
217+
// Close for embedfs file is a no-op.
218+
func (f *file) Close() error {
219+
return nil
220+
}
221+
222+
// Lock for embedfs file is a no-op.
223+
func (f *file) Lock() error {
224+
return nil
225+
}
226+
227+
// Unlock for embedfs file is a no-op.
228+
func (f *file) Unlock() error {
229+
return nil
230+
}
231+
232+
// Truncate is not supported.
233+
//
234+
// Calls will always return billy.ErrReadOnly.
235+
func (f *file) Truncate(_ int64) error {
236+
return billy.ErrReadOnly
237+
}
238+
239+
// Write is not supported.
240+
//
241+
// Calls will always return billy.ErrReadOnly.
242+
func (f *file) Write(_ []byte) (int, error) {
243+
return 0, billy.ErrReadOnly
244+
}
245+
246+
// WriteAt is not supported.
247+
//
248+
// Calls will always return billy.ErrReadOnly.
249+
func (f *file) WriteAt([]byte, int64) (int, error) {
250+
return 0, billy.ErrReadOnly
251+
}

0 commit comments

Comments
 (0)