%PDF- %PDF-
| Direktori : /proc/self/root/home/waritko/go/src/github.com/odeke-em/drive/src/ |
| Current File : //proc/self/root/home/waritko/go/src/github.com/odeke-em/drive/src/move.go |
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package drive
import (
"fmt"
"os"
"path/filepath"
)
type moveOpt struct {
src string
dest string
byId bool
keepParent bool
}
type RenameMode uint
const (
RenameNone RenameMode = 1 << iota
RenameRemote
RenameLocal
)
func canRenameLocal(rmode RenameMode) bool {
return (rmode & RenameLocal) != 0
}
func canRenameRemote(rmode RenameMode) bool {
return (rmode & RenameRemote) != 0
}
func cannotRename(rmode RenameMode) bool {
return !(canRenameLocal(rmode) || canRenameRemote(rmode))
}
func (g *Commands) Move(byId, keepParent bool) error {
argc := len(g.opts.Sources)
if argc < 2 {
return invalidArgumentsErr(fmt.Errorf("move: expected <src> [src...] <dest>, instead got: %v", g.opts.Sources))
}
rest, dest := g.opts.Sources[:argc-1], g.opts.Sources[argc-1]
var composedError error = nil
for _, src := range rest {
prefix := commonPrefix(src, dest)
// Trying to nest a parent into its child
if prefix == src {
return illogicalStateErr(fmt.Errorf("%s cannot be nested into %s", src, dest))
}
opt := moveOpt{
src: src,
dest: dest,
byId: byId,
keepParent: keepParent,
}
if err := g.move(&opt); err != nil {
message := fmt.Sprintf("move: %s: %v", src, err)
composedError = reComposeError(composedError, message)
}
}
return composedError
}
func (g *Commands) move(opt *moveOpt) (err error) {
var newParent, remSrc *File
srcResolver := g.rem.FindByPath
if opt.byId {
srcResolver = g.rem.FindById
}
if remSrc, err = srcResolver(opt.src); err != nil {
return makeErrorWithStatus(fmt.Sprintf("src('%s')", opt.src), err, StatusRemoteLookupFailed)
}
if remSrc == nil {
return nonExistantRemoteErr(fmt.Errorf("src: '%s' could not be found", opt.src))
}
if newParent, err = g.rem.FindByPath(opt.dest); err != nil {
return remoteLookupErr(fmt.Errorf("dest: '%s' %v", opt.dest, err))
}
if newParent == nil || !newParent.IsDir {
return illogicalStateErr(fmt.Errorf("dest: '%s' must be an existent folder", opt.dest))
}
if !opt.byId {
parentPath := g.parentPather(opt.src)
oldParent, parErr := g.rem.FindByPath(parentPath)
if parErr != nil && parErr != ErrPathNotExists {
return parErr
}
// TODO: If oldParent is not found, retry since it may have been moved temporarily at least
if oldParent != nil && oldParent.Id == newParent.Id {
return illogicalStateErr(fmt.Errorf("src and dest are the same srcParentId %s destParentId %s",
customQuote(oldParent.Id), customQuote(newParent.Id)))
}
}
newFullPath := filepath.Join(opt.dest, remSrc.Name)
// Check for a duplicate
var dupCheck *File
dupCheck, err = g.rem.FindByPath(newFullPath)
if err != nil && err != ErrPathNotExists {
return err
}
if dupCheck != nil {
if dupCheck.Id == remSrc.Id { // Trying to move to self
return illogicalStateErr(fmt.Errorf("move: trying to move fileId:%s to self fileId:%s", customQuote(dupCheck.Id), customQuote(remSrc.Id)))
}
if !g.opts.Force {
return overwriteAttemptedErr(fmt.Errorf("%s already exists. Use `%s` flag to override this behaviour", newFullPath, ForceKey))
}
}
// Avoid self-nesting
if remSrc.Id == newParent.Id {
return illogicalStateErr(fmt.Errorf("move: cannot move '%s' to itself", opt.src))
}
if err = g.rem.insertParent(remSrc.Id, newParent.Id); err != nil {
return err
}
if opt.byId { // TODO: Also take out this current parent
return nil
}
if !opt.keepParent {
err = g.removeParent(remSrc.Id, opt.src)
}
return err
}
func (g *Commands) removeParent(fileId, relToRootPath string) error {
parentPath := g.parentPather(relToRootPath)
parent, pErr := g.rem.FindByPath(parentPath)
if pErr != nil {
return pErr
}
if parent == nil {
return illogicalStateErr(fmt.Errorf("non existent parent '%s' for src", parentPath))
}
return g.rem.removeParent(fileId, parent.Id)
}
func (g *Commands) renameLocal(oldRelToRootPath, newRelToRootPath string) error {
oldLocalPath := g.context.AbsPathOf(oldRelToRootPath)
newLocalPath := g.context.AbsPathOf(newRelToRootPath)
return os.Rename(oldLocalPath, newLocalPath)
}
func (g *Commands) renameRemote(remSrcId, parentPath, newName string) error {
urlBoundName := urlToPath(newName, true)
newFullPath := filepath.Join(parentPath, urlBoundName)
dupCheck, err := g.rem.FindByPath(newFullPath)
if err == nil && dupCheck != nil {
if dupCheck.Id == remSrcId { // Trying to rename self
return nil
}
if !g.opts.Force {
return overwriteAttemptedErr(fmt.Errorf("%s already exists. Use `%s` flag to override this behaviour", newFullPath, ForceKey))
}
}
_, err = g.rem.rename(remSrcId, newName)
return err
}
func (g *Commands) Rename(byId bool) error {
if len(g.opts.Sources) < 2 {
return invalidArgumentsErr(fmt.Errorf("rename: expecting <src> <newname>"))
}
if cannotRename(g.opts.RenameMode) {
return illogicalStateErr(fmt.Errorf("no rename mode set, set either `local` or `remote` mode"))
}
src := g.opts.Sources[0]
resolver := g.rem.FindByPath
if byId {
resolver = g.rem.FindById
}
remSrc, err := resolver(src)
if err != nil {
return fmt.Errorf("%s: %v", src, err)
}
if remSrc == nil {
return illogicalStateErr(fmt.Errorf("%s does not exist", src))
}
var parentPath string
if !byId {
parentPath = g.parentPather(src)
} else {
parentPath = g.opts.Path
}
newName := g.opts.Sources[1]
renamedRemote := false
if canRenameRemote(g.opts.RenameMode) {
if err = g.renameRemote(remSrc.Id, parentPath, newName); err != nil {
return err
}
renamedRemote = true
}
if canRenameLocal(g.opts.RenameMode) {
// Otherwise time to mirror the same changes locally
oldRelToRootPath := sepJoin(RemoteSeparator, parentPath, remSrc.Name)
newRelToRootPath := sepJoin(RemoteSeparator, parentPath, newName)
err = g.renameLocal(oldRelToRootPath, newRelToRootPath)
if err != nil {
switch {
// If a local path doesn't exist we shouldn't try to report an error if
// they've already just renamed the remote path.
// A common case is renaming a remote file that isn't on disk.
// See Issue https://github.com/odeke-em/drive/issues/826.
case NotExist(err) && renamedRemote:
err = nil
default:
return err
}
}
}
return err
}