github.com/richardwilkes/toolbox@v1.121.0/xio/fs/move.go (about)

     1  // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, version 2.0. If a copy of the MPL was not distributed with
     5  // this file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  //
     7  // This Source Code Form is "Incompatible With Secondary Licenses", as
     8  // defined by the Mozilla Public License, version 2.0.
     9  
    10  package fs
    11  
    12  import (
    13  	"io"
    14  	"os"
    15  
    16  	"github.com/richardwilkes/toolbox/errs"
    17  	"github.com/richardwilkes/toolbox/xio"
    18  )
    19  
    20  // MoveFile moves a file in the file system or across volumes, using rename if possible, but falling back to copying the
    21  // file if not. This will error if either src or dst are not regular files.
    22  func MoveFile(src, dst string) (err error) {
    23  	var srcInfo, dstInfo os.FileInfo
    24  	srcInfo, err = os.Stat(src)
    25  	if err != nil {
    26  		return errs.Wrap(err)
    27  	}
    28  	if !srcInfo.Mode().IsRegular() {
    29  		return errs.Newf("%s is not a regular file", src)
    30  	}
    31  	dstInfo, err = os.Stat(dst)
    32  	if err != nil {
    33  		if !os.IsNotExist(err) {
    34  			return errs.Wrap(err)
    35  		}
    36  	} else {
    37  		if !dstInfo.Mode().IsRegular() {
    38  			return errs.Newf("%s is not a regular file", dst)
    39  		}
    40  		if os.SameFile(srcInfo, dstInfo) {
    41  			return nil
    42  		}
    43  	}
    44  	if os.Rename(src, dst) == nil {
    45  		return nil
    46  	}
    47  	var in, out *os.File
    48  	out, err = os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, srcInfo.Mode())
    49  	if err != nil {
    50  		return errs.Wrap(err)
    51  	}
    52  	defer func() {
    53  		if closeErr := out.Close(); closeErr != nil && err == nil {
    54  			err = closeErr
    55  		}
    56  	}()
    57  	if in, err = os.Open(src); err != nil {
    58  		err = errs.Wrap(err)
    59  		return
    60  	}
    61  	_, err = io.Copy(out, in)
    62  	xio.CloseIgnoringErrors(in)
    63  	if err != nil {
    64  		err = errs.Wrap(err)
    65  		return
    66  	}
    67  	if err = os.Remove(src); err != nil {
    68  		err = errs.Wrap(err)
    69  	}
    70  	return
    71  }