forked from TrueCloudLab/rclone
Get rid of LimitedFs - FIXME needs docs on copying single files
If remote:path points to a file make NewFs return a sentinel error fs.ErrorIsFile and an Fs which points to the parent. Use this to remove the LimitedFs and just add this file to the --files-from list. This means that server side operations can be used also. Fixes #518 Fixes #545
This commit is contained in:
parent
5a3b109e25
commit
1a87b69376
26 changed files with 101 additions and 239 deletions
41
fs/fs.go
41
fs/fs.go
|
@ -42,6 +42,7 @@ var (
|
|||
ErrorLevelNotSupported = errors.New("level value not supported")
|
||||
ErrorListAborted = errors.New("list aborted")
|
||||
ErrorListOnlyRoot = errors.New("can only list from root")
|
||||
ErrorIsFile = errors.New("is a file not a directory")
|
||||
)
|
||||
|
||||
// RegInfo provides information about a filesystem
|
||||
|
@ -51,8 +52,8 @@ type RegInfo struct {
|
|||
// Description of this fs - defaults to Name
|
||||
Description string
|
||||
// Create a new file system. If root refers to an existing
|
||||
// object, then it should return a Fs which only returns that
|
||||
// object.
|
||||
// object, then it should return a Fs which which points to
|
||||
// the parent of that object and ErrorIsFile.
|
||||
NewFs func(name string, root string) (Fs, error)
|
||||
// Function to call to help with config
|
||||
Config func(string)
|
||||
|
@ -347,6 +348,26 @@ func Find(name string) (*RegInfo, error) {
|
|||
// Pattern to match an rclone url
|
||||
var matcher = regexp.MustCompile(`^([\w_ -]+):(.*)$`)
|
||||
|
||||
// ParseRemote deconstructs a path into configName, fsPath, looking up
|
||||
// the fsName in the config file (returning NotFoundInConfigFile if not found)
|
||||
func ParseRemote(path string) (fsInfo *RegInfo, configName, fsPath string, err error) {
|
||||
parts := matcher.FindStringSubmatch(path)
|
||||
var fsName string
|
||||
fsName, configName, fsPath = "local", "local", path
|
||||
if parts != nil && !isDriveLetter(parts[1]) {
|
||||
configName, fsPath = parts[1], parts[2]
|
||||
var err error
|
||||
fsName, err = ConfigFile.GetValue(configName, "type")
|
||||
if err != nil {
|
||||
return nil, "", "", ErrorNotFoundInConfigFile
|
||||
}
|
||||
}
|
||||
// change native directory separators to / if there are any
|
||||
fsPath = filepath.ToSlash(fsPath)
|
||||
fsInfo, err = Find(fsName)
|
||||
return fsInfo, configName, fsPath, err
|
||||
}
|
||||
|
||||
// NewFs makes a new Fs object from the path
|
||||
//
|
||||
// The path is of the form remote:path
|
||||
|
@ -357,23 +378,11 @@ var matcher = regexp.MustCompile(`^([\w_ -]+):(.*)$`)
|
|||
// On Windows avoid single character remote names as they can be mixed
|
||||
// up with drive letters.
|
||||
func NewFs(path string) (Fs, error) {
|
||||
parts := matcher.FindStringSubmatch(path)
|
||||
fsName, configName, fsPath := "local", "local", path
|
||||
if parts != nil && !isDriveLetter(parts[1]) {
|
||||
configName, fsPath = parts[1], parts[2]
|
||||
var err error
|
||||
fsName, err = ConfigFile.GetValue(configName, "type")
|
||||
if err != nil {
|
||||
return nil, ErrorNotFoundInConfigFile
|
||||
}
|
||||
}
|
||||
fs, err := Find(fsName)
|
||||
fsInfo, configName, fsPath, err := ParseRemote(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// change native directory separators to / if there are any
|
||||
fsPath = filepath.ToSlash(fsPath)
|
||||
return fs.NewFs(configName, fsPath)
|
||||
return fsInfo.NewFs(configName, fsPath)
|
||||
}
|
||||
|
||||
// DebugLogger - logs to Stdout
|
||||
|
|
147
fs/limited.go
147
fs/limited.go
|
@ -1,147 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Limited defines a Fs which can only return the Objects passed in
|
||||
// from the Fs passed in
|
||||
type Limited struct {
|
||||
objects []Object
|
||||
fs Fs
|
||||
}
|
||||
|
||||
// NewLimited maks a limited Fs limited to the objects passed in
|
||||
func NewLimited(fs Fs, objects ...Object) Fs {
|
||||
f := &Limited{
|
||||
objects: objects,
|
||||
fs: fs,
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// Name is name of the remote (as passed into NewFs)
|
||||
func (f *Limited) Name() string {
|
||||
return f.fs.Name() // return name of underlying remote
|
||||
}
|
||||
|
||||
// Root is the root of the remote (as passed into NewFs)
|
||||
func (f *Limited) Root() string {
|
||||
return f.fs.Root() // return root of underlying remote
|
||||
}
|
||||
|
||||
// String returns a description of the FS
|
||||
func (f *Limited) String() string {
|
||||
return fmt.Sprintf("%s limited to %d objects", f.fs.String(), len(f.objects))
|
||||
}
|
||||
|
||||
// List the Fs into a channel
|
||||
func (f *Limited) List(opts ListOpts, dir string) {
|
||||
defer opts.Finished()
|
||||
if dir != "" {
|
||||
opts.SetError(ErrorListOnlyRoot)
|
||||
return
|
||||
}
|
||||
for _, obj := range f.objects {
|
||||
if opts.Add(obj) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewFsObject finds the Object at remote. Returns nil if can't be found
|
||||
func (f *Limited) NewFsObject(remote string) Object {
|
||||
for _, obj := range f.objects {
|
||||
if obj.Remote() == remote {
|
||||
return obj
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Put in to the remote path with the modTime given of the given size
|
||||
//
|
||||
// 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
|
||||
func (f *Limited) Put(in io.Reader, src ObjectInfo) (Object, error) {
|
||||
remote := src.Remote()
|
||||
obj := f.NewFsObject(remote)
|
||||
if obj == nil {
|
||||
return nil, errors.Errorf("can't create %q in limited fs", remote)
|
||||
}
|
||||
return obj, obj.Update(in, src)
|
||||
}
|
||||
|
||||
// Mkdir make the directory (container, bucket)
|
||||
func (f *Limited) Mkdir() error {
|
||||
// All directories are already made - just ignore
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rmdir removes the directory (container, bucket) if empty
|
||||
func (f *Limited) Rmdir() error {
|
||||
// Ignore this in a limited fs
|
||||
return nil
|
||||
}
|
||||
|
||||
// Precision of the ModTimes in this Fs
|
||||
func (f *Limited) Precision() time.Duration {
|
||||
return f.fs.Precision()
|
||||
}
|
||||
|
||||
// Hashes returns the supported hash sets.
|
||||
func (f *Limited) Hashes() HashSet {
|
||||
return f.fs.Hashes()
|
||||
}
|
||||
|
||||
// Copy src to this remote using server side copy operations.
|
||||
//
|
||||
// This is stored with the remote path given
|
||||
//
|
||||
// It returns the destination Object and a possible error
|
||||
//
|
||||
// Will only be called if src.Fs().Name() == f.Name()
|
||||
//
|
||||
// If it isn't possible then return fs.ErrorCantCopy
|
||||
func (f *Limited) Copy(src Object, remote string) (Object, error) {
|
||||
fCopy, ok := f.fs.(Copier)
|
||||
if !ok {
|
||||
return nil, ErrorCantCopy
|
||||
}
|
||||
return fCopy.Copy(src, remote)
|
||||
}
|
||||
|
||||
// Move src to this remote using server side move operations.
|
||||
//
|
||||
// This is stored with the remote path given
|
||||
//
|
||||
// It returns the destination Object and a possible error
|
||||
//
|
||||
// Will only be called if src.Fs().Name() == f.Name()
|
||||
//
|
||||
// If it isn't possible then return fs.ErrorCantMove
|
||||
func (f *Limited) Move(src Object, remote string) (Object, error) {
|
||||
fMove, ok := f.fs.(Mover)
|
||||
if !ok {
|
||||
return nil, ErrorCantMove
|
||||
}
|
||||
return fMove.Move(src, remote)
|
||||
}
|
||||
|
||||
// UnWrap returns the Fs that this Fs is wrapping
|
||||
func (f *Limited) UnWrap() Fs {
|
||||
return f.fs
|
||||
}
|
||||
|
||||
// Check the interfaces are satisfied
|
||||
var (
|
||||
_ Fs = (*Limited)(nil)
|
||||
_ Copier = (*Limited)(nil)
|
||||
_ Mover = (*Limited)(nil)
|
||||
_ UnWrapper = (*Limited)(nil)
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue