rclone/fs/types.go
Nick Craig-Wood cc634213a5 fs: define the optional interface SetMetadata and implement it in wrapping backends
This also implements backend integration tests for the feature
2024-05-14 12:51:03 +01:00

363 lines
10 KiB
Go

// Filesystem related types and interfaces
// Note that optional interfaces are found in features.go
package fs
import (
"context"
"io"
"time"
"github.com/rclone/rclone/fs/hash"
)
// Fs is the interface a cloud storage system must provide
type Fs interface {
Info
// 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.
List(ctx context.Context, dir string) (entries DirEntries, err error)
// NewObject finds the Object at remote. If it can't be found
// it returns the error ErrorObjectNotFound.
//
// If remote points to a directory then it should return
// ErrorIsDir if possible without doing any extra work,
// otherwise ErrorObjectNotFound.
NewObject(ctx context.Context, remote string) (Object, error)
// Put in to the remote path with the modTime given of the given size
//
// When called from outside an Fs by rclone, src.Size() will always be >= 0.
// But for unknown-sized objects (indicated by src.Size() == -1), Put should either
// return an error or upload it properly (rather than e.g. calling panic).
//
// 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
Put(ctx context.Context, in io.Reader, src ObjectInfo, options ...OpenOption) (Object, error)
// Mkdir makes the directory (container, bucket)
//
// Shouldn't return an error if it already exists
Mkdir(ctx context.Context, dir string) error
// Rmdir removes the directory (container, bucket) if empty
//
// Return an error if it doesn't exist or isn't empty
Rmdir(ctx context.Context, dir string) error
}
// Info provides a read only interface to information about a filesystem.
type Info interface {
// Name of the remote (as passed into NewFs)
Name() string
// Root of the remote (as passed into NewFs)
Root() string
// String returns a description of the FS
String() string
// Precision of the ModTimes in this Fs
Precision() time.Duration
// Returns the supported hash types of the filesystem
Hashes() hash.Set
// Features returns the optional features of this Fs
Features() *Features
}
// Object is a filesystem like object provided by an Fs
type Object interface {
ObjectInfo
// SetModTime sets the metadata on the object to set the modification date
SetModTime(ctx context.Context, t time.Time) error
// Open opens the file for read. Call Close() on the returned io.ReadCloser
Open(ctx context.Context, options ...OpenOption) (io.ReadCloser, error)
// Update in to the object with the modTime given of the given size
//
// When called from outside an Fs by rclone, src.Size() will always be >= 0.
// But for unknown-sized objects (indicated by src.Size() == -1), Upload should either
// return an error or update the object properly (rather than e.g. calling panic).
Update(ctx context.Context, in io.Reader, src ObjectInfo, options ...OpenOption) error
// Removes this object
Remove(ctx context.Context) error
}
// ObjectInfo provides read only information about an object.
type ObjectInfo interface {
DirEntry
// Hash returns the selected checksum of the file
// If no checksum is available it returns ""
Hash(ctx context.Context, ty hash.Type) (string, error)
// Storable says whether this object can be stored
Storable() bool
}
// DirEntry provides read only information about the common subset of
// a Dir or Object. These are returned from directory listings - type
// assert them into the correct type.
type DirEntry interface {
// Fs returns read only access to the Fs that this object is part of
Fs() Info
// String returns a description of the Object
String() string
// Remote returns the remote path
Remote() string
// ModTime returns the modification date of the file
// It should return a best guess if one isn't available
ModTime(context.Context) time.Time
// Size returns the size of the file
Size() int64
}
// Directory is a filesystem like directory provided by an Fs
type Directory interface {
DirEntry
// Items returns the count of items in this directory or this
// directory and subdirectories if known, -1 for unknown
Items() int64
// ID returns the internal ID of this directory if known, or
// "" otherwise
ID() string
}
// FullDirectory contains all the optional interfaces for Directory
//
// Use for checking making wrapping Directories implement everything
type FullDirectory interface {
Directory
Metadataer
SetMetadataer
SetModTimer
}
// MimeTyper is an optional interface for Object
type MimeTyper interface {
// MimeType returns the content type of the Object if
// known, or "" if not
MimeType(ctx context.Context) string
}
// IDer is an optional interface for Object
type IDer interface {
// ID returns the ID of the Object if known, or "" if not
ID() string
}
// ParentIDer is an optional interface for Object
type ParentIDer interface {
// ParentID returns the ID of the parent directory if known or nil if not
ParentID() string
}
// ObjectUnWrapper is an optional interface for Object
type ObjectUnWrapper interface {
// UnWrap returns the Object that this Object is wrapping or
// nil if it isn't wrapping anything
UnWrap() Object
}
// SetTierer is an optional interface for Object
type SetTierer interface {
// SetTier performs changing storage tier of the Object if
// multiple storage classes supported
SetTier(tier string) error
}
// GetTierer is an optional interface for Object
type GetTierer interface {
// GetTier returns storage tier or class of the Object
GetTier() string
}
// Metadataer is an optional interface for DirEntry
type Metadataer interface {
// Metadata returns metadata for an DirEntry
//
// It should return nil if there is no Metadata
Metadata(ctx context.Context) (Metadata, error)
}
// SetMetadataer is an optional interface for DirEntry
type SetMetadataer interface {
// SetMetadata sets metadata for an DirEntry
//
// It should return fs.ErrorNotImplemented if it can't set metadata
SetMetadata(ctx context.Context, metadata Metadata) error
}
// SetModTimer is an optional interface for Directory.
//
// Object implements this as part of its requires set of interfaces.
type SetModTimer interface {
// SetModTime sets the metadata on the DirEntry to set the modification date
//
// If there is any other metadata it does not overwrite it.
SetModTime(ctx context.Context, t time.Time) error
}
// FullObjectInfo contains all the read-only optional interfaces
//
// Use for checking making wrapping ObjectInfos implement everything
type FullObjectInfo interface {
ObjectInfo
MimeTyper
IDer
ObjectUnWrapper
GetTierer
Metadataer
}
// FullObject contains all the optional interfaces for Object
//
// Use for checking making wrapping Objects implement everything
type FullObject interface {
Object
MimeTyper
IDer
ObjectUnWrapper
GetTierer
SetTierer
Metadataer
SetMetadataer
}
// ObjectOptionalInterfaces returns the names of supported and
// unsupported optional interfaces for an Object
func ObjectOptionalInterfaces(o Object) (supported, unsupported []string) {
store := func(ok bool, name string) {
if ok {
supported = append(supported, name)
} else {
unsupported = append(unsupported, name)
}
}
_, ok := o.(MimeTyper)
store(ok, "MimeType")
_, ok = o.(IDer)
store(ok, "ID")
_, ok = o.(ObjectUnWrapper)
store(ok, "UnWrap")
_, ok = o.(SetTierer)
store(ok, "SetTier")
_, ok = o.(GetTierer)
store(ok, "GetTier")
_, ok = o.(Metadataer)
store(ok, "Metadata")
_, ok = o.(SetMetadataer)
store(ok, "SetMetadata")
return supported, unsupported
}
// DirectoryOptionalInterfaces returns the names of supported and
// unsupported optional interfaces for a Directory
func DirectoryOptionalInterfaces(d Directory) (supported, unsupported []string) {
store := func(ok bool, name string) {
if ok {
supported = append(supported, name)
} else {
unsupported = append(unsupported, name)
}
}
_, ok := d.(Metadataer)
store(ok, "Metadata")
_, ok = d.(SetMetadataer)
store(ok, "SetMetadata")
_, ok = d.(SetModTimer)
store(ok, "SetModTime")
return supported, unsupported
}
// ListRCallback defines a callback function for ListR to use
//
// It is called for each tranche of entries read from the listing and
// if it returns an error, the listing stops.
type ListRCallback func(entries DirEntries) error
// ListRFn is defines the call used to recursively list a directory
type ListRFn func(ctx context.Context, dir string, callback ListRCallback) error
// NewUsageValue makes a valid value
func NewUsageValue(value int64) *int64 {
p := new(int64)
*p = value
return p
}
// Usage is returned by the About call
//
// If a value is nil then it isn't supported by that backend
type Usage struct {
Total *int64 `json:"total,omitempty"` // quota of bytes that can be used
Used *int64 `json:"used,omitempty"` // bytes in use
Trashed *int64 `json:"trashed,omitempty"` // bytes in trash
Other *int64 `json:"other,omitempty"` // other usage e.g. gmail in drive
Free *int64 `json:"free,omitempty"` // bytes which can be uploaded before reaching the quota
Objects *int64 `json:"objects,omitempty"` // objects in the storage system
}
// WriterAtCloser wraps io.WriterAt and io.Closer
type WriterAtCloser interface {
io.WriterAt
io.Closer
}
type unknownFs struct{}
// Name of the remote (as passed into NewFs)
func (unknownFs) Name() string { return "unknown" }
// Root of the remote (as passed into NewFs)
func (unknownFs) Root() string { return "" }
// String returns a description of the FS
func (unknownFs) String() string { return "unknown" }
// Precision of the ModTimes in this Fs
func (unknownFs) Precision() time.Duration { return ModTimeNotSupported }
// Returns the supported hash types of the filesystem
func (unknownFs) Hashes() hash.Set { return hash.Set(hash.None) }
// Features returns the optional features of this Fs
func (unknownFs) Features() *Features { return &Features{} }
// Unknown holds an Info for an unknown Fs
//
// This is used when we need an Fs but don't have one.
var Unknown Info = unknownFs{}