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  )