github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/object/object.go (about) 1 // Package object defines some useful Objects 2 package object 3 4 import ( 5 "bytes" 6 "context" 7 "errors" 8 "io" 9 "time" 10 11 "github.com/rclone/rclone/fs" 12 "github.com/rclone/rclone/fs/hash" 13 ) 14 15 // StaticObjectInfo is an ObjectInfo which can be constructed from scratch 16 type StaticObjectInfo struct { 17 remote string 18 modTime time.Time 19 size int64 20 storable bool 21 hashes map[hash.Type]string 22 fs fs.Info 23 meta fs.Metadata 24 mimeType string 25 } 26 27 // NewStaticObjectInfo returns a static ObjectInfo 28 // If hashes is nil and fs is not nil, the hash map will be replaced with 29 // empty hashes of the types supported by the fs. 30 func NewStaticObjectInfo(remote string, modTime time.Time, size int64, storable bool, hashes map[hash.Type]string, f fs.Info) *StaticObjectInfo { 31 info := &StaticObjectInfo{ 32 remote: remote, 33 modTime: modTime, 34 size: size, 35 storable: storable, 36 hashes: hashes, 37 fs: f, 38 } 39 if f != nil && hashes == nil { 40 set := f.Hashes().Array() 41 info.hashes = make(map[hash.Type]string) 42 for _, ht := range set { 43 info.hashes[ht] = "" 44 } 45 } 46 if f == nil { 47 info.fs = MemoryFs 48 } 49 return info 50 } 51 52 // WithMetadata adds meta to the ObjectInfo 53 func (i *StaticObjectInfo) WithMetadata(meta fs.Metadata) *StaticObjectInfo { 54 i.meta = meta 55 return i 56 } 57 58 // WithMimeType adds meta to the ObjectInfo 59 func (i *StaticObjectInfo) WithMimeType(mimeType string) *StaticObjectInfo { 60 i.mimeType = mimeType 61 return i 62 } 63 64 // Fs returns read only access to the Fs that this object is part of 65 func (i *StaticObjectInfo) Fs() fs.Info { 66 return i.fs 67 } 68 69 // Remote returns the remote path 70 func (i *StaticObjectInfo) Remote() string { 71 return i.remote 72 } 73 74 // String returns a description of the Object 75 func (i *StaticObjectInfo) String() string { 76 return i.remote 77 } 78 79 // ModTime returns the modification date of the file 80 func (i *StaticObjectInfo) ModTime(ctx context.Context) time.Time { 81 return i.modTime 82 } 83 84 // Size returns the size of the file 85 func (i *StaticObjectInfo) Size() int64 { 86 return i.size 87 } 88 89 // Storable says whether this object can be stored 90 func (i *StaticObjectInfo) Storable() bool { 91 return i.storable 92 } 93 94 // Hash returns the requested hash of the contents 95 func (i *StaticObjectInfo) Hash(ctx context.Context, h hash.Type) (string, error) { 96 if len(i.hashes) == 0 { 97 return "", hash.ErrUnsupported 98 } 99 if hash, ok := i.hashes[h]; ok { 100 return hash, nil 101 } 102 return "", hash.ErrUnsupported 103 } 104 105 // Metadata on the object 106 func (i *StaticObjectInfo) Metadata(ctx context.Context) (fs.Metadata, error) { 107 return i.meta, nil 108 } 109 110 // MimeType returns the content type of the Object if 111 // known, or "" if not 112 func (i *StaticObjectInfo) MimeType(ctx context.Context) string { 113 return i.mimeType 114 } 115 116 // Check interfaces 117 var ( 118 _ fs.ObjectInfo = (*StaticObjectInfo)(nil) 119 _ fs.Metadataer = (*StaticObjectInfo)(nil) 120 _ fs.MimeTyper = (*StaticObjectInfo)(nil) 121 ) 122 123 // MemoryFs is an in memory Fs, it only supports FsInfo and Put 124 var MemoryFs memoryFs 125 126 // memoryFs is an in memory fs 127 type memoryFs struct{} 128 129 // Name of the remote (as passed into NewFs) 130 func (memoryFs) Name() string { return "memory" } 131 132 // Root of the remote (as passed into NewFs) 133 func (memoryFs) Root() string { return "" } 134 135 // String returns a description of the FS 136 func (memoryFs) String() string { return "memory" } 137 138 // Precision of the ModTimes in this Fs 139 func (memoryFs) Precision() time.Duration { return time.Nanosecond } 140 141 // Returns the supported hash types of the filesystem 142 func (memoryFs) Hashes() hash.Set { return hash.Supported() } 143 144 // Features returns the optional features of this Fs 145 func (memoryFs) Features() *fs.Features { return &fs.Features{} } 146 147 // List the objects and directories in dir into entries. The 148 // entries can be returned in any order but should be for a 149 // complete directory. 150 // 151 // dir should be "" to list the root, and should not have 152 // trailing slashes. 153 // 154 // This should return ErrDirNotFound if the directory isn't 155 // found. 156 func (memoryFs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) { 157 return nil, nil 158 } 159 160 // NewObject finds the Object at remote. If it can't be found 161 // it returns the error ErrorObjectNotFound. 162 func (memoryFs) NewObject(ctx context.Context, remote string) (fs.Object, error) { 163 return nil, fs.ErrorObjectNotFound 164 } 165 166 // Put in to the remote path with the modTime given of the given size 167 // 168 // May create the object even if it returns an error - if so 169 // will return the object and the error, otherwise will return 170 // nil and the error 171 func (memoryFs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) { 172 o := NewMemoryObject(src.Remote(), src.ModTime(ctx), nil) 173 return o, o.Update(ctx, in, src, options...) 174 } 175 176 // Mkdir makes the directory (container, bucket) 177 // 178 // Shouldn't return an error if it already exists 179 func (memoryFs) Mkdir(ctx context.Context, dir string) error { 180 return errors.New("memoryFs: can't make directory") 181 } 182 183 // Rmdir removes the directory (container, bucket) if empty 184 // 185 // Return an error if it doesn't exist or isn't empty 186 func (memoryFs) Rmdir(ctx context.Context, dir string) error { 187 return fs.ErrorDirNotFound 188 } 189 190 var _ fs.Fs = MemoryFs 191 192 // MemoryObject is an in memory object 193 type MemoryObject struct { 194 remote string 195 modTime time.Time 196 content []byte 197 meta fs.Metadata 198 } 199 200 // NewMemoryObject returns an in memory Object with the modTime and content passed in 201 func NewMemoryObject(remote string, modTime time.Time, content []byte) *MemoryObject { 202 return &MemoryObject{ 203 remote: remote, 204 modTime: modTime, 205 content: content, 206 } 207 } 208 209 // WithMetadata adds meta to the MemoryObject 210 func (o *MemoryObject) WithMetadata(meta fs.Metadata) *MemoryObject { 211 o.meta = meta 212 return o 213 } 214 215 // Content returns the underlying buffer 216 func (o *MemoryObject) Content() []byte { 217 return o.content 218 } 219 220 // Fs returns read only access to the Fs that this object is part of 221 func (o *MemoryObject) Fs() fs.Info { 222 return MemoryFs 223 } 224 225 // Remote returns the remote path 226 func (o *MemoryObject) Remote() string { 227 return o.remote 228 } 229 230 // String returns a description of the Object 231 func (o *MemoryObject) String() string { 232 return o.remote 233 } 234 235 // ModTime returns the modification date of the file 236 func (o *MemoryObject) ModTime(ctx context.Context) time.Time { 237 return o.modTime 238 } 239 240 // Size returns the size of the file 241 func (o *MemoryObject) Size() int64 { 242 return int64(len(o.content)) 243 } 244 245 // Storable says whether this object can be stored 246 func (o *MemoryObject) Storable() bool { 247 return true 248 } 249 250 // Hash returns the requested hash of the contents 251 func (o *MemoryObject) Hash(ctx context.Context, h hash.Type) (string, error) { 252 hash, err := hash.NewMultiHasherTypes(hash.Set(h)) 253 if err != nil { 254 return "", err 255 } 256 _, err = hash.Write(o.content) 257 if err != nil { 258 return "", err 259 } 260 return hash.Sums()[h], nil 261 } 262 263 // SetModTime sets the metadata on the object to set the modification date 264 func (o *MemoryObject) SetModTime(ctx context.Context, modTime time.Time) error { 265 o.modTime = modTime 266 return nil 267 } 268 269 // Open opens the file for read. Call Close() on the returned io.ReadCloser 270 func (o *MemoryObject) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) { 271 content := o.content 272 for _, option := range options { 273 switch x := option.(type) { 274 case *fs.RangeOption: 275 content = o.content[x.Start:x.End] 276 case *fs.SeekOption: 277 content = o.content[x.Offset:] 278 default: 279 if option.Mandatory() { 280 fs.Logf(o, "Unsupported mandatory option: %v", option) 281 } 282 } 283 } 284 return io.NopCloser(bytes.NewBuffer(content)), nil 285 } 286 287 // Update in to the object with the modTime given of the given size 288 // 289 // This re-uses the internal buffer if at all possible. 290 func (o *MemoryObject) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) { 291 size := src.Size() 292 if size == 0 { 293 o.content = nil 294 } else if size < 0 || int64(cap(o.content)) < size { 295 o.content, err = io.ReadAll(in) 296 } else { 297 o.content = o.content[:size] 298 _, err = io.ReadFull(in, o.content) 299 } 300 o.modTime = src.ModTime(ctx) 301 return err 302 } 303 304 // Remove this object 305 func (o *MemoryObject) Remove(ctx context.Context) error { 306 return errors.New("memoryObject.Remove not supported") 307 } 308 309 // Metadata on the object 310 func (o *MemoryObject) Metadata(ctx context.Context) (fs.Metadata, error) { 311 return o.meta, nil 312 } 313 314 // Check interfaces 315 var ( 316 _ fs.Object = (*MemoryObject)(nil) 317 _ fs.Metadataer = (*MemoryObject)(nil) 318 )