github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/cache/locations/fs.go (about) 1 package locations 2 3 import ( 4 "errors" 5 "io" 6 "os" 7 "path/filepath" 8 "sync" 9 10 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" 11 ) 12 13 const FS_PERMISSIONS = 0755 14 15 // Each Storer implementation is global and thread-safe to save resources 16 // when reading/writing large number of items 17 var fsInstance *fileSystem 18 var fsInitOnce sync.Once 19 20 // fsReadWriteCloser is a wrapper around os.File that removes any file locks 21 // when Close() is called 22 type fsReadWriteCloser struct { 23 *os.File 24 } 25 26 // Close closes the underlying os.File and removes file lock 27 func (f *fsReadWriteCloser) Close() error { 28 // We unlock file when the caller is done 29 if err := file.Unlock(f.File); err != nil { 30 return err 31 } 32 return f.File.Close() 33 } 34 35 type fileSystem struct{} 36 37 // FileSystem returns an instance of fileSystem Storer, ready to be used 38 func FileSystem() (*fileSystem, error) { 39 fsInitOnce.Do(func() { 40 fsInstance = new(fileSystem) 41 }) 42 return fsInstance, nil 43 } 44 45 // Writer returns io.WriterCloser for the item at given path 46 func (l *fileSystem) Writer(path string) (io.WriteCloser, error) { 47 if err := l.makeParentDirectories(path); err != nil { 48 return nil, err 49 } 50 51 output, err := file.WaitOnLock(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE) 52 if err != nil { 53 return nil, err 54 } 55 56 return &fsReadWriteCloser{output}, nil 57 } 58 59 // Reader returns io.ReaderCloser for the item at given path 60 func (l *fileSystem) Reader(path string) (io.ReadCloser, error) { 61 input, err := file.WaitOnLock(path, os.O_RDWR) 62 if err != nil { 63 if errors.Is(err, os.ErrNotExist) { 64 return nil, ErrNotFound 65 } 66 return nil, err 67 } 68 69 return &fsReadWriteCloser{input}, nil 70 } 71 72 // Remove removes the item at given path 73 func (l *fileSystem) Remove(path string) error { 74 if err := os.Remove(path); err != nil { 75 return err 76 } 77 78 dirPath, _ := filepath.Split(path) 79 if empty, _ := file.IsFolderEmpty(dirPath); empty { 80 // we ignore the error 81 os.RemoveAll(dirPath) 82 } 83 return nil 84 } 85 86 func (l *fileSystem) Stat(path string) (*ItemInfo, error) { 87 info, err := os.Stat(path) 88 if err != nil { 89 return nil, err 90 } 91 92 return &ItemInfo{ 93 fileSize: int(info.Size()), 94 name: info.Name(), 95 }, err 96 } 97 98 func (l *fileSystem) makeParentDirectories(path string) error { 99 dirPath, _ := filepath.Split(path) 100 return os.MkdirAll(dirPath, FS_PERMISSIONS) 101 }