1package filesystems
2
3import (
4 "io/fs"
5 "strings"
6 "sync"
7)
8
9const (
10 DefaultFilesystemKey = "default"
11)
12
13var DefaultFilesystem = &wrapperFs{key: DefaultFilesystemKey, FS: OsFS{}}
14
15// wrapperFs exists so can easily add to wrapperFs down the line
16type wrapperFs struct {
17 key string
18 fs.FS
19}
20
21// FilesystemMap stores a map of filesystems
22// the empty key will be overwritten to be the default key
23// it includes a default filesystem, based off the os fs
24type FilesystemMap struct {
25 m sync.Map
26}
27
28// note that the first invocation of key cannot be called in a racy context.
29func (f *FilesystemMap) key(k string) string {
30 if k == "" {
31 k = DefaultFilesystemKey
32 }
33 return k
34}
35
36// Register will add the filesystem with key to later be retrieved
37// A call with a nil fs will call unregister, ensuring that a call to Default() will never be nil
38func (f *FilesystemMap) Register(k string, v fs.FS) {
39 k = f.key(k)
40 if v == nil {
41 f.Unregister(k)
42 return
43 }
44 f.m.Store(k, &wrapperFs{key: k, FS: v})
45}
46
47// Unregister will remove the filesystem with key from the filesystem map
48// if the key is the default key, it will set the default to the osFS instead of deleting it
49// modules should call this on cleanup to be safe
50func (f *FilesystemMap) Unregister(k string) {
51 k = f.key(k)
52 if k == DefaultFilesystemKey {
53 f.m.Store(k, DefaultFilesystem)
54 } else {
55 f.m.Delete(k)
56 }
57}
58
59// Get will get a filesystem with a given key
60func (f *FilesystemMap) Get(k string) (v fs.FS, ok bool) {
61 k = f.key(k)
62 c, ok := f.m.Load(strings.TrimSpace(k))
63 if !ok {
64 if k == DefaultFilesystemKey {
65 f.m.Store(k, DefaultFilesystem)
66 return DefaultFilesystem, true
67 }
68 return nil, ok
69 }
70 return c.(fs.FS), true
71}
72
73// Default will get the default filesystem in the filesystem map
74func (f *FilesystemMap) Default() fs.FS {
75 val, _ := f.Get(DefaultFilesystemKey)
76 return val
77}
78