diff --git a/fs/features.go b/fs/features.go index a6ec21e30..828f3b94b 100644 --- a/fs/features.go +++ b/fs/features.go @@ -13,27 +13,32 @@ import ( // Features describe the optional features of the Fs type Features struct { // Feature flags, whether Fs - CaseInsensitive bool // has case insensitive files - DuplicateFiles bool // allows duplicate files - ReadMimeType bool // can read the mime type of objects - WriteMimeType bool // can set the mime type of objects - CanHaveEmptyDirectories bool // can have empty directories - BucketBased bool // is bucket based (like s3, swift, etc.) - BucketBasedRootOK bool // is bucket based and can use from root - SetTier bool // allows set tier functionality on objects - GetTier bool // allows to retrieve storage tier of objects - ServerSideAcrossConfigs bool // can server-side copy between different remotes of the same type - IsLocal bool // is the local backend - SlowModTime bool // if calling ModTime() generally takes an extra transaction - SlowHash bool // if calling Hash() generally takes an extra transaction - ReadMetadata bool // can read metadata from objects - WriteMetadata bool // can write metadata to objects - UserMetadata bool // can read/write general purpose metadata - FilterAware bool // can make use of filters if provided for listing - PartialUploads bool // uploaded file can appear incomplete on the fs while it's being uploaded - NoMultiThreading bool // set if can't have multiplethreads on one download open - Overlay bool // this wraps one or more backends to add functionality - ChunkWriterDoesntSeek bool // set if the chunk writer doesn't need to read the data more than once + CaseInsensitive bool // has case insensitive files + DuplicateFiles bool // allows duplicate files + ReadMimeType bool // can read the mime type of objects + WriteMimeType bool // can set the mime type of objects + CanHaveEmptyDirectories bool // can have empty directories + BucketBased bool // is bucket based (like s3, swift, etc.) + BucketBasedRootOK bool // is bucket based and can use from root + SetTier bool // allows set tier functionality on objects + GetTier bool // allows to retrieve storage tier of objects + ServerSideAcrossConfigs bool // can server-side copy between different remotes of the same type + IsLocal bool // is the local backend + SlowModTime bool // if calling ModTime() generally takes an extra transaction + SlowHash bool // if calling Hash() generally takes an extra transaction + ReadMetadata bool // can read metadata from objects + WriteMetadata bool // can write metadata to objects + UserMetadata bool // can read/write general purpose metadata + ReadDirMetadata bool // can read metadata from directories (implements Directory.Metadata) + WriteDirMetadata bool // can write metadata to directories (implements Directory.SetMetadata) + WriteDirSetModTime bool // can write metadata to directories (implements Directory.SetModTime) + UserDirMetadata bool // can read/write general purpose metadata to/from directories + DirModTimeUpdatesOnWrite bool // indicate writing files to a directory updates its modtime + FilterAware bool // can make use of filters if provided for listing + PartialUploads bool // uploaded file can appear incomplete on the fs while it's being uploaded + NoMultiThreading bool // set if can't have multiplethreads on one download open + Overlay bool // this wraps one or more backends to add functionality + ChunkWriterDoesntSeek bool // set if the chunk writer doesn't need to read the data more than once // Purge all files in the directory specified // @@ -75,6 +80,15 @@ type Features struct { // If destination exists then return fs.ErrorDirExists DirMove func(ctx context.Context, src Fs, srcRemote, dstRemote string) error + // MkdirMetadata makes the directory passed in as dir. + // + // It shouldn't return an error if it already exists. + // + // If the metadata is not nil it is set. + // + // It returns the directory that was created. + MkdirMetadata func(ctx context.Context, dir string, metadata Metadata) (Directory, error) + // ChangeNotify calls the passed function with a path // that has had changes. If the implementation // uses polling, it should adhere to the given interval. @@ -274,6 +288,9 @@ func (ft *Features) Fill(ctx context.Context, f Fs) *Features { if do, ok := f.(DirMover); ok { ft.DirMove = do.DirMove } + if do, ok := f.(MkdirMetadataer); ok { + ft.MkdirMetadata = do.MkdirMetadata + } if do, ok := f.(ChangeNotifier); ok { ft.ChangeNotify = do.ChangeNotify } @@ -348,6 +365,11 @@ func (ft *Features) Mask(ctx context.Context, f Fs) *Features { ft.ReadMetadata = ft.ReadMetadata && mask.ReadMetadata ft.WriteMetadata = ft.WriteMetadata && mask.WriteMetadata ft.UserMetadata = ft.UserMetadata && mask.UserMetadata + ft.ReadDirMetadata = ft.ReadDirMetadata && mask.ReadDirMetadata + ft.WriteDirMetadata = ft.WriteDirMetadata && mask.WriteDirMetadata + ft.WriteDirSetModTime = ft.WriteDirSetModTime && mask.WriteDirSetModTime + ft.UserDirMetadata = ft.UserDirMetadata && mask.UserDirMetadata + ft.DirModTimeUpdatesOnWrite = ft.DirModTimeUpdatesOnWrite && mask.DirModTimeUpdatesOnWrite ft.CanHaveEmptyDirectories = ft.CanHaveEmptyDirectories && mask.CanHaveEmptyDirectories ft.BucketBased = ft.BucketBased && mask.BucketBased ft.BucketBasedRootOK = ft.BucketBasedRootOK && mask.BucketBasedRootOK @@ -374,6 +396,9 @@ func (ft *Features) Mask(ctx context.Context, f Fs) *Features { if mask.DirMove == nil { ft.DirMove = nil } + if mask.MkdirMetadata == nil { + ft.MkdirMetadata = nil + } if mask.ChangeNotify == nil { ft.ChangeNotify = nil } @@ -505,6 +530,18 @@ type DirMover interface { DirMove(ctx context.Context, src Fs, srcRemote, dstRemote string) error } +// MkdirMetadataer is an optional interface for Fs +type MkdirMetadataer interface { + // MkdirMetadata makes the directory passed in as dir. + // + // It shouldn't return an error if it already exists. + // + // If the metadata is not nil it is set. + // + // It returns the directory that was created. + MkdirMetadata(ctx context.Context, dir string, metadata Metadata) (Directory, error) +} + // ChangeNotifier is an optional interface for Fs type ChangeNotifier interface { // ChangeNotify calls the passed function with a path diff --git a/fs/types.go b/fs/types.go index 88ff5e8cd..16afe79d8 100644 --- a/fs/types.go +++ b/fs/types.go @@ -144,6 +144,16 @@ type Directory interface { 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 @@ -183,14 +193,32 @@ type GetTierer interface { GetTier() string } -// Metadataer is an optional interface for Object +// Metadataer is an optional interface for DirEntry type Metadataer interface { - // Metadata returns metadata for an object + // 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 @@ -248,6 +276,29 @@ func ObjectOptionalInterfaces(o Object) (supported, unsupported []string) { 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