Eric Bower
·
2025-04-18
memory.go
1package storage
2
3import (
4 "fmt"
5 "io"
6 "os"
7 "path/filepath"
8 "strings"
9 "sync"
10 "time"
11
12 "github.com/picosh/pico/pkg/send/utils"
13)
14
15type StorageMemory struct {
16 storage map[string]map[string]string
17 mu sync.RWMutex
18}
19
20var _ ObjectStorage = &StorageMemory{}
21var _ ObjectStorage = (*StorageMemory)(nil)
22
23func NewStorageMemory(st map[string]map[string]string) (*StorageMemory, error) {
24 return &StorageMemory{
25 storage: st,
26 }, nil
27}
28
29func (s *StorageMemory) GetBucket(name string) (Bucket, error) {
30 s.mu.RLock()
31 defer s.mu.RUnlock()
32
33 bucket := Bucket{
34 Name: name,
35 Path: name,
36 }
37
38 _, ok := s.storage[name]
39 if !ok {
40 return bucket, fmt.Errorf("bucket does not exist")
41 }
42
43 return bucket, nil
44}
45
46func (s *StorageMemory) UpsertBucket(name string) (Bucket, error) {
47 bucket, err := s.GetBucket(name)
48 if err == nil {
49 return bucket, nil
50 }
51
52 s.mu.Lock()
53 defer s.mu.Unlock()
54
55 s.storage[name] = map[string]string{}
56 return bucket, nil
57}
58
59func (s *StorageMemory) GetBucketQuota(bucket Bucket) (uint64, error) {
60 s.mu.RLock()
61 defer s.mu.RUnlock()
62
63 objects := s.storage[bucket.Path]
64 size := 0
65 for _, val := range objects {
66 size += len([]byte(val))
67 }
68 return uint64(size), nil
69}
70
71func (s *StorageMemory) DeleteBucket(bucket Bucket) error {
72 s.mu.Lock()
73 defer s.mu.Unlock()
74
75 delete(s.storage, bucket.Path)
76 return nil
77}
78
79func (s *StorageMemory) GetObject(bucket Bucket, fpath string) (utils.ReadAndReaderAtCloser, *ObjectInfo, error) {
80 s.mu.RLock()
81 defer s.mu.RUnlock()
82
83 if !strings.HasPrefix(fpath, "/") {
84 fpath = "/" + fpath
85 }
86
87 objInfo := &ObjectInfo{
88 LastModified: time.Time{},
89 Metadata: nil,
90 }
91
92 dat, ok := s.storage[bucket.Path][fpath]
93 if !ok {
94 return nil, objInfo, fmt.Errorf("object does not exist: %s", fpath)
95 }
96
97 objInfo.Size = int64(len([]byte(dat)))
98 reader := utils.NopReadAndReaderAtCloser(strings.NewReader(dat))
99 return reader, objInfo, nil
100}
101
102func (s *StorageMemory) PutObject(bucket Bucket, fpath string, contents io.Reader, entry *utils.FileEntry) (string, int64, error) {
103 s.mu.Lock()
104 defer s.mu.Unlock()
105
106 d, err := io.ReadAll(contents)
107 if err != nil {
108 return "", 0, err
109 }
110
111 s.storage[bucket.Path][fpath] = string(d)
112 return fmt.Sprintf("%s%s", bucket.Path, fpath), int64(len(d)), nil
113}
114
115func (s *StorageMemory) DeleteObject(bucket Bucket, fpath string) error {
116 s.mu.Lock()
117 defer s.mu.Unlock()
118
119 delete(s.storage[bucket.Path], fpath)
120 return nil
121}
122
123func (s *StorageMemory) ListBuckets() ([]string, error) {
124 s.mu.RLock()
125 defer s.mu.RUnlock()
126
127 buckets := []string{}
128 for key := range s.storage {
129 buckets = append(buckets, key)
130 }
131 return buckets, nil
132}
133
134func (s *StorageMemory) ListObjects(bucket Bucket, dir string, recursive bool) ([]os.FileInfo, error) {
135 s.mu.RLock()
136 defer s.mu.RUnlock()
137
138 var fileList []os.FileInfo
139
140 resolved := dir
141
142 if !strings.HasPrefix(resolved, "/") {
143 resolved = "/" + resolved
144 }
145
146 objects := s.storage[bucket.Path]
147 // dir is actually an object
148 oval, ok := objects[resolved]
149 if ok {
150 fileList = append(fileList, &utils.VirtualFile{
151 FName: filepath.Base(resolved),
152 FIsDir: false,
153 FSize: int64(len([]byte(oval))),
154 FModTime: time.Time{},
155 })
156 return fileList, nil
157 }
158
159 for key, val := range objects {
160 if !strings.HasPrefix(key, resolved) {
161 continue
162 }
163
164 rep := strings.Replace(key, resolved, "", 1)
165 fdir := filepath.Dir(rep)
166 fname := filepath.Base(rep)
167 paths := strings.Split(fdir, "/")
168
169 if fdir == "/" {
170 ffname := filepath.Base(resolved)
171 fileList = append(fileList, &utils.VirtualFile{
172 FName: ffname,
173 FIsDir: true,
174 })
175 }
176
177 for _, p := range paths {
178 if p == "" || p == "/" || p == "." {
179 continue
180 }
181 fileList = append(fileList, &utils.VirtualFile{
182 FName: p,
183 FIsDir: true,
184 })
185 }
186
187 trimRes := strings.TrimSuffix(resolved, "/")
188 dirKey := filepath.Dir(key)
189 if recursive {
190 fileList = append(fileList, &utils.VirtualFile{
191 FName: fname,
192 FIsDir: false,
193 FSize: int64(len([]byte(val))),
194 FModTime: time.Time{},
195 })
196 } else if resolved == dirKey || trimRes == dirKey {
197 fileList = append(fileList, &utils.VirtualFile{
198 FName: fname,
199 FIsDir: false,
200 FSize: int64(len([]byte(val))),
201 FModTime: time.Time{},
202 })
203 }
204 }
205
206 return fileList, nil
207}