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

@ -29,6 +29,11 @@ type Features struct {
ReadMetadata bool // can read metadata from objects ReadMetadata bool // can read metadata from objects
WriteMetadata bool // can write metadata to objects WriteMetadata bool // can write metadata to objects
UserMetadata bool // can read/write general purpose metadata 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 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 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 NoMultiThreading bool // set if can't have multiplethreads on one download open
@ -75,6 +80,15 @@ type Features struct {
// If destination exists then return fs.ErrorDirExists // If destination exists then return fs.ErrorDirExists
DirMove func(ctx context.Context, src Fs, srcRemote, dstRemote string) error 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 // ChangeNotify calls the passed function with a path
// that has had changes. If the implementation // that has had changes. If the implementation
// uses polling, it should adhere to the given interval. // 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 { if do, ok := f.(DirMover); ok {
ft.DirMove = do.DirMove ft.DirMove = do.DirMove
} }
if do, ok := f.(MkdirMetadataer); ok {
ft.MkdirMetadata = do.MkdirMetadata
}
if do, ok := f.(ChangeNotifier); ok { if do, ok := f.(ChangeNotifier); ok {
ft.ChangeNotify = do.ChangeNotify 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.ReadMetadata = ft.ReadMetadata && mask.ReadMetadata
ft.WriteMetadata = ft.WriteMetadata && mask.WriteMetadata ft.WriteMetadata = ft.WriteMetadata && mask.WriteMetadata
ft.UserMetadata = ft.UserMetadata && mask.UserMetadata 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.CanHaveEmptyDirectories = ft.CanHaveEmptyDirectories && mask.CanHaveEmptyDirectories
ft.BucketBased = ft.BucketBased && mask.BucketBased ft.BucketBased = ft.BucketBased && mask.BucketBased
ft.BucketBasedRootOK = ft.BucketBasedRootOK && mask.BucketBasedRootOK ft.BucketBasedRootOK = ft.BucketBasedRootOK && mask.BucketBasedRootOK
@ -374,6 +396,9 @@ func (ft *Features) Mask(ctx context.Context, f Fs) *Features {
if mask.DirMove == nil { if mask.DirMove == nil {
ft.DirMove = nil ft.DirMove = nil
} }
if mask.MkdirMetadata == nil {
ft.MkdirMetadata = nil
}
if mask.ChangeNotify == nil { if mask.ChangeNotify == nil {
ft.ChangeNotify = nil ft.ChangeNotify = nil
} }
@ -505,6 +530,18 @@ type DirMover interface {
DirMove(ctx context.Context, src Fs, srcRemote, dstRemote string) error 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 // ChangeNotifier is an optional interface for Fs
type ChangeNotifier interface { type ChangeNotifier interface {
// ChangeNotify calls the passed function with a path // ChangeNotify calls the passed function with a path

View File

@ -144,6 +144,16 @@ type Directory interface {
ID() string 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 // MimeTyper is an optional interface for Object
type MimeTyper interface { type MimeTyper interface {
// MimeType returns the content type of the Object if // MimeType returns the content type of the Object if
@ -183,14 +193,32 @@ type GetTierer interface {
GetTier() string GetTier() string
} }
// Metadataer is an optional interface for Object // Metadataer is an optional interface for DirEntry
type Metadataer interface { type Metadataer interface {
// Metadata returns metadata for an object // Metadata returns metadata for an DirEntry
// //
// It should return nil if there is no Metadata // It should return nil if there is no Metadata
Metadata(ctx context.Context) (Metadata, error) 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 // FullObjectInfo contains all the read-only optional interfaces
// //
// Use for checking making wrapping ObjectInfos implement everything // Use for checking making wrapping ObjectInfos implement everything
@ -248,6 +276,29 @@ func ObjectOptionalInterfaces(o Object) (supported, unsupported []string) {
return supported, unsupported 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 // ListRCallback defines a callback function for ListR to use
// //
// It is called for each tranche of entries read from the listing and // It is called for each tranche of entries read from the listing and