fs: add Directory Metadata flags for backends and interfaces

Add backend flags
- ReadDirMetadata
- WriteDirMetadata
- WriteDirSetModTime
- UserDirMetadata
- DirModTimeUpdatesOnWrite

Add Metadata/SetMetadata for directories.

Add MkdirMetadata optional feature
This commit is contained in:
Nick Craig-Wood 2024-02-05 17:19:43 +00:00
parent 6da52d76a7
commit a4cadd1128
2 changed files with 111 additions and 23 deletions

View file

@ -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

View file

@ -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