2018-01-12 17:30:54 +01:00
|
|
|
// Package object defines some useful Objects
|
|
|
|
package object
|
2017-06-30 14:37:29 +02:00
|
|
|
|
2017-12-01 16:15:44 +01:00
|
|
|
import (
|
|
|
|
"bytes"
|
2019-06-17 10:34:30 +02:00
|
|
|
"context"
|
2017-12-01 16:15:44 +01:00
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"time"
|
2018-01-12 17:30:54 +01:00
|
|
|
|
2019-07-28 19:47:38 +02:00
|
|
|
"github.com/rclone/rclone/fs"
|
|
|
|
"github.com/rclone/rclone/fs/hash"
|
2017-12-01 16:15:44 +01:00
|
|
|
)
|
2017-06-30 14:37:29 +02:00
|
|
|
|
2022-11-08 18:41:27 +01:00
|
|
|
// StaticObjectInfo is an ObjectInfo which can be constructed from scratch
|
|
|
|
type StaticObjectInfo struct {
|
|
|
|
remote string
|
|
|
|
modTime time.Time
|
|
|
|
size int64
|
|
|
|
storable bool
|
|
|
|
hashes map[hash.Type]string
|
|
|
|
fs fs.Info
|
|
|
|
meta fs.Metadata
|
|
|
|
mimeType string
|
|
|
|
}
|
|
|
|
|
2017-06-30 14:37:29 +02:00
|
|
|
// NewStaticObjectInfo returns a static ObjectInfo
|
|
|
|
// If hashes is nil and fs is not nil, the hash map will be replaced with
|
|
|
|
// empty hashes of the types supported by the fs.
|
2022-11-08 18:41:27 +01:00
|
|
|
func NewStaticObjectInfo(remote string, modTime time.Time, size int64, storable bool, hashes map[hash.Type]string, f fs.Info) *StaticObjectInfo {
|
|
|
|
info := &StaticObjectInfo{
|
2017-06-30 14:37:29 +02:00
|
|
|
remote: remote,
|
|
|
|
modTime: modTime,
|
|
|
|
size: size,
|
|
|
|
storable: storable,
|
|
|
|
hashes: hashes,
|
2022-11-08 18:41:27 +01:00
|
|
|
fs: f,
|
2017-06-30 14:37:29 +02:00
|
|
|
}
|
2022-11-08 18:41:27 +01:00
|
|
|
if f != nil && hashes == nil {
|
|
|
|
set := f.Hashes().Array()
|
2018-01-12 17:30:54 +01:00
|
|
|
info.hashes = make(map[hash.Type]string)
|
2017-06-30 14:37:29 +02:00
|
|
|
for _, ht := range set {
|
|
|
|
info.hashes[ht] = ""
|
|
|
|
}
|
|
|
|
}
|
2024-05-15 16:55:05 +02:00
|
|
|
if f == nil {
|
|
|
|
info.fs = MemoryFs
|
|
|
|
}
|
2017-06-30 14:37:29 +02:00
|
|
|
return info
|
|
|
|
}
|
|
|
|
|
2022-11-08 18:41:27 +01:00
|
|
|
// WithMetadata adds meta to the ObjectInfo
|
|
|
|
func (i *StaticObjectInfo) WithMetadata(meta fs.Metadata) *StaticObjectInfo {
|
|
|
|
i.meta = meta
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithMimeType adds meta to the ObjectInfo
|
|
|
|
func (i *StaticObjectInfo) WithMimeType(mimeType string) *StaticObjectInfo {
|
|
|
|
i.mimeType = mimeType
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fs returns read only access to the Fs that this object is part of
|
|
|
|
func (i *StaticObjectInfo) Fs() fs.Info {
|
|
|
|
return i.fs
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remote returns the remote path
|
|
|
|
func (i *StaticObjectInfo) Remote() string {
|
|
|
|
return i.remote
|
2017-06-30 14:37:29 +02:00
|
|
|
}
|
|
|
|
|
2022-11-08 18:41:27 +01:00
|
|
|
// String returns a description of the Object
|
|
|
|
func (i *StaticObjectInfo) String() string {
|
|
|
|
return i.remote
|
|
|
|
}
|
|
|
|
|
|
|
|
// ModTime returns the modification date of the file
|
|
|
|
func (i *StaticObjectInfo) ModTime(ctx context.Context) time.Time {
|
|
|
|
return i.modTime
|
|
|
|
}
|
|
|
|
|
|
|
|
// Size returns the size of the file
|
|
|
|
func (i *StaticObjectInfo) Size() int64 {
|
|
|
|
return i.size
|
|
|
|
}
|
|
|
|
|
|
|
|
// Storable says whether this object can be stored
|
|
|
|
func (i *StaticObjectInfo) Storable() bool {
|
|
|
|
return i.storable
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hash returns the requested hash of the contents
|
|
|
|
func (i *StaticObjectInfo) Hash(ctx context.Context, h hash.Type) (string, error) {
|
2017-06-30 14:37:29 +02:00
|
|
|
if len(i.hashes) == 0 {
|
2018-01-18 21:27:52 +01:00
|
|
|
return "", hash.ErrUnsupported
|
2017-06-30 14:37:29 +02:00
|
|
|
}
|
|
|
|
if hash, ok := i.hashes[h]; ok {
|
|
|
|
return hash, nil
|
|
|
|
}
|
2018-01-18 21:27:52 +01:00
|
|
|
return "", hash.ErrUnsupported
|
2017-06-30 14:37:29 +02:00
|
|
|
}
|
2017-12-01 16:15:44 +01:00
|
|
|
|
2022-11-08 18:41:27 +01:00
|
|
|
// Metadata on the object
|
|
|
|
func (i *StaticObjectInfo) Metadata(ctx context.Context) (fs.Metadata, error) {
|
|
|
|
return i.meta, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MimeType returns the content type of the Object if
|
|
|
|
// known, or "" if not
|
|
|
|
func (i *StaticObjectInfo) MimeType(ctx context.Context) string {
|
|
|
|
return i.mimeType
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check interfaces
|
|
|
|
var (
|
|
|
|
_ fs.ObjectInfo = (*StaticObjectInfo)(nil)
|
|
|
|
_ fs.Metadataer = (*StaticObjectInfo)(nil)
|
|
|
|
_ fs.MimeTyper = (*StaticObjectInfo)(nil)
|
|
|
|
)
|
|
|
|
|
2017-12-01 16:15:44 +01:00
|
|
|
// MemoryFs is an in memory Fs, it only supports FsInfo and Put
|
|
|
|
var MemoryFs memoryFs
|
|
|
|
|
|
|
|
// memoryFs is an in memory fs
|
|
|
|
type memoryFs struct{}
|
|
|
|
|
|
|
|
// Name of the remote (as passed into NewFs)
|
|
|
|
func (memoryFs) Name() string { return "memory" }
|
|
|
|
|
|
|
|
// Root of the remote (as passed into NewFs)
|
|
|
|
func (memoryFs) Root() string { return "" }
|
|
|
|
|
|
|
|
// String returns a description of the FS
|
|
|
|
func (memoryFs) String() string { return "memory" }
|
|
|
|
|
|
|
|
// Precision of the ModTimes in this Fs
|
|
|
|
func (memoryFs) Precision() time.Duration { return time.Nanosecond }
|
|
|
|
|
|
|
|
// Returns the supported hash types of the filesystem
|
2019-09-23 15:32:36 +02:00
|
|
|
func (memoryFs) Hashes() hash.Set { return hash.Supported() }
|
2017-12-01 16:15:44 +01:00
|
|
|
|
|
|
|
// Features returns the optional features of this Fs
|
2018-01-12 17:30:54 +01:00
|
|
|
func (memoryFs) Features() *fs.Features { return &fs.Features{} }
|
2017-12-01 16:15:44 +01:00
|
|
|
|
|
|
|
// List the objects and directories in dir into entries. The
|
|
|
|
// entries can be returned in any order but should be for a
|
|
|
|
// complete directory.
|
|
|
|
//
|
|
|
|
// dir should be "" to list the root, and should not have
|
|
|
|
// trailing slashes.
|
|
|
|
//
|
|
|
|
// This should return ErrDirNotFound if the directory isn't
|
|
|
|
// found.
|
2019-06-17 10:34:30 +02:00
|
|
|
func (memoryFs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
|
2017-12-01 16:15:44 +01:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewObject finds the Object at remote. If it can't be found
|
|
|
|
// it returns the error ErrorObjectNotFound.
|
2019-06-17 10:34:30 +02:00
|
|
|
func (memoryFs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
|
2018-01-12 17:30:54 +01:00
|
|
|
return nil, fs.ErrorObjectNotFound
|
2017-12-01 16:15:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Put in to the remote path with the modTime given of the given size
|
|
|
|
//
|
|
|
|
// May create the object even if it returns an error - if so
|
|
|
|
// will return the object and the error, otherwise will return
|
|
|
|
// nil and the error
|
2019-06-17 10:34:30 +02:00
|
|
|
func (memoryFs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
|
|
|
|
o := NewMemoryObject(src.Remote(), src.ModTime(ctx), nil)
|
|
|
|
return o, o.Update(ctx, in, src, options...)
|
2017-12-01 16:15:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Mkdir makes the directory (container, bucket)
|
|
|
|
//
|
|
|
|
// Shouldn't return an error if it already exists
|
2019-06-17 10:34:30 +02:00
|
|
|
func (memoryFs) Mkdir(ctx context.Context, dir string) error {
|
2017-12-01 16:15:44 +01:00
|
|
|
return errors.New("memoryFs: can't make directory")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rmdir removes the directory (container, bucket) if empty
|
|
|
|
//
|
|
|
|
// Return an error if it doesn't exist or isn't empty
|
2019-06-17 10:34:30 +02:00
|
|
|
func (memoryFs) Rmdir(ctx context.Context, dir string) error {
|
2018-01-12 17:30:54 +01:00
|
|
|
return fs.ErrorDirNotFound
|
2017-12-01 16:15:44 +01:00
|
|
|
}
|
|
|
|
|
2018-01-12 17:30:54 +01:00
|
|
|
var _ fs.Fs = MemoryFs
|
2017-12-01 16:15:44 +01:00
|
|
|
|
|
|
|
// MemoryObject is an in memory object
|
|
|
|
type MemoryObject struct {
|
|
|
|
remote string
|
|
|
|
modTime time.Time
|
|
|
|
content []byte
|
2022-11-08 18:41:27 +01:00
|
|
|
meta fs.Metadata
|
2024-05-15 18:48:05 +02:00
|
|
|
fs fs.Fs
|
2017-12-01 16:15:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewMemoryObject returns an in memory Object with the modTime and content passed in
|
|
|
|
func NewMemoryObject(remote string, modTime time.Time, content []byte) *MemoryObject {
|
|
|
|
return &MemoryObject{
|
|
|
|
remote: remote,
|
|
|
|
modTime: modTime,
|
|
|
|
content: content,
|
2024-05-15 18:48:05 +02:00
|
|
|
fs: MemoryFs,
|
2017-12-01 16:15:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-08 18:41:27 +01:00
|
|
|
// WithMetadata adds meta to the MemoryObject
|
|
|
|
func (o *MemoryObject) WithMetadata(meta fs.Metadata) *MemoryObject {
|
|
|
|
o.meta = meta
|
|
|
|
return o
|
|
|
|
}
|
|
|
|
|
2017-12-01 16:15:44 +01:00
|
|
|
// Content returns the underlying buffer
|
|
|
|
func (o *MemoryObject) Content() []byte {
|
|
|
|
return o.content
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fs returns read only access to the Fs that this object is part of
|
2018-01-12 17:30:54 +01:00
|
|
|
func (o *MemoryObject) Fs() fs.Info {
|
2024-05-15 18:48:05 +02:00
|
|
|
return o.fs
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetFs sets the Fs that this memory object thinks it is part of
|
|
|
|
// It will ignore nil f
|
|
|
|
func (o *MemoryObject) SetFs(f fs.Fs) *MemoryObject {
|
|
|
|
if f != nil {
|
|
|
|
o.fs = f
|
|
|
|
}
|
|
|
|
return o
|
2017-12-01 16:15:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Remote returns the remote path
|
|
|
|
func (o *MemoryObject) Remote() string {
|
|
|
|
return o.remote
|
|
|
|
}
|
|
|
|
|
|
|
|
// String returns a description of the Object
|
|
|
|
func (o *MemoryObject) String() string {
|
|
|
|
return o.remote
|
|
|
|
}
|
|
|
|
|
|
|
|
// ModTime returns the modification date of the file
|
2019-06-17 10:34:30 +02:00
|
|
|
func (o *MemoryObject) ModTime(ctx context.Context) time.Time {
|
2017-12-01 16:15:44 +01:00
|
|
|
return o.modTime
|
|
|
|
}
|
|
|
|
|
|
|
|
// Size returns the size of the file
|
|
|
|
func (o *MemoryObject) Size() int64 {
|
|
|
|
return int64(len(o.content))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Storable says whether this object can be stored
|
|
|
|
func (o *MemoryObject) Storable() bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hash returns the requested hash of the contents
|
2019-06-17 10:34:30 +02:00
|
|
|
func (o *MemoryObject) Hash(ctx context.Context, h hash.Type) (string, error) {
|
2018-01-12 17:30:54 +01:00
|
|
|
hash, err := hash.NewMultiHasherTypes(hash.Set(h))
|
2017-12-01 16:15:44 +01:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
_, err = hash.Write(o.content)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return hash.Sums()[h], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetModTime sets the metadata on the object to set the modification date
|
2019-06-17 10:34:30 +02:00
|
|
|
func (o *MemoryObject) SetModTime(ctx context.Context, modTime time.Time) error {
|
2017-12-01 16:15:44 +01:00
|
|
|
o.modTime = modTime
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open opens the file for read. Call Close() on the returned io.ReadCloser
|
2019-06-17 10:34:30 +02:00
|
|
|
func (o *MemoryObject) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) {
|
2017-12-01 16:15:44 +01:00
|
|
|
content := o.content
|
|
|
|
for _, option := range options {
|
|
|
|
switch x := option.(type) {
|
2018-01-12 17:30:54 +01:00
|
|
|
case *fs.RangeOption:
|
2017-12-01 16:15:44 +01:00
|
|
|
content = o.content[x.Start:x.End]
|
2018-01-12 17:30:54 +01:00
|
|
|
case *fs.SeekOption:
|
2017-12-01 16:15:44 +01:00
|
|
|
content = o.content[x.Offset:]
|
|
|
|
default:
|
|
|
|
if option.Mandatory() {
|
2018-01-12 17:30:54 +01:00
|
|
|
fs.Logf(o, "Unsupported mandatory option: %v", option)
|
2017-12-01 16:15:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-20 16:38:02 +02:00
|
|
|
return io.NopCloser(bytes.NewBuffer(content)), nil
|
2017-12-01 16:15:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update in to the object with the modTime given of the given size
|
|
|
|
//
|
|
|
|
// This re-uses the internal buffer if at all possible.
|
2019-06-17 10:34:30 +02:00
|
|
|
func (o *MemoryObject) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) {
|
2017-12-01 16:15:44 +01:00
|
|
|
size := src.Size()
|
|
|
|
if size == 0 {
|
|
|
|
o.content = nil
|
|
|
|
} else if size < 0 || int64(cap(o.content)) < size {
|
2022-08-20 16:38:02 +02:00
|
|
|
o.content, err = io.ReadAll(in)
|
2017-12-01 16:15:44 +01:00
|
|
|
} else {
|
|
|
|
o.content = o.content[:size]
|
|
|
|
_, err = io.ReadFull(in, o.content)
|
|
|
|
}
|
2019-06-17 10:34:30 +02:00
|
|
|
o.modTime = src.ModTime(ctx)
|
2017-12-01 16:15:44 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove this object
|
2019-06-17 10:34:30 +02:00
|
|
|
func (o *MemoryObject) Remove(ctx context.Context) error {
|
2017-12-01 16:15:44 +01:00
|
|
|
return errors.New("memoryObject.Remove not supported")
|
|
|
|
}
|
2022-11-08 18:41:27 +01:00
|
|
|
|
|
|
|
// Metadata on the object
|
|
|
|
func (o *MemoryObject) Metadata(ctx context.Context) (fs.Metadata, error) {
|
|
|
|
return o.meta, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check interfaces
|
|
|
|
var (
|
|
|
|
_ fs.Object = (*MemoryObject)(nil)
|
|
|
|
_ fs.Metadataer = (*MemoryObject)(nil)
|
|
|
|
)
|