%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/waritko/go/src/github.com/odeke-em/drive/src/
Upload File :
Create Path :
Current File : //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
}

Zerion Mini Shell 1.0