mirror of
https://github.com/ManyakRus/crud_generator.git
synced 2024-12-22 00:36:41 +02:00
172 lines
5.3 KiB
Go
172 lines
5.3 KiB
Go
package copy
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
|
|
"golang.org/x/sync/semaphore"
|
|
)
|
|
|
|
// Options specifies optional actions on copying.
|
|
type Options struct {
|
|
|
|
// OnSymlink can specify what to do on symlink
|
|
OnSymlink func(src string) SymlinkAction
|
|
|
|
// OnDirExists can specify what to do when there is a directory already existing in destination.
|
|
OnDirExists func(src, dest string) DirExistsAction
|
|
|
|
// OnErr lets called decide whether or not to continue on particular copy error.
|
|
OnError func(src, dest string, err error) error
|
|
|
|
// Skip can specify which files should be skipped
|
|
Skip func(srcinfo os.FileInfo, src, dest string) (bool, error)
|
|
|
|
// Specials includes special files to be copied. default false.
|
|
Specials bool
|
|
|
|
// AddPermission to every entities,
|
|
// NO MORE THAN 0777
|
|
// @OBSOLETE
|
|
// Use `PermissionControl = AddPermission(perm)` instead
|
|
AddPermission os.FileMode
|
|
|
|
// PermissionControl can preserve or even add permission to
|
|
// every entries, for example
|
|
//
|
|
// opt.PermissionControl = AddPermission(0222)
|
|
//
|
|
// See permission_control.go for more detail.
|
|
PermissionControl PermissionControlFunc
|
|
|
|
// Sync file after copy.
|
|
// Useful in case when file must be on the disk
|
|
// (in case crash happens, for example),
|
|
// at the expense of some performance penalty
|
|
Sync bool
|
|
|
|
// Preserve the atime and the mtime of the entries.
|
|
// On linux we can preserve only up to 1 millisecond accuracy.
|
|
PreserveTimes bool
|
|
|
|
// Preserve the uid and the gid of all entries.
|
|
PreserveOwner bool
|
|
|
|
// The byte size of the buffer to use for copying files.
|
|
// If zero, the internal default buffer of 32KB is used.
|
|
// See https://golang.org/pkg/io/#CopyBuffer for more information.
|
|
CopyBufferSize uint
|
|
|
|
// If you want to add some limitation on reading src file,
|
|
// you can wrap the src and provide new reader,
|
|
// such as `RateLimitReader` in the test case.
|
|
WrapReader func(src io.Reader) io.Reader
|
|
|
|
// If given, copy.Copy refers to this fs.FS instead of the OS filesystem.
|
|
// e.g., You can use embed.FS to copy files from embedded filesystem.
|
|
FS fs.FS
|
|
|
|
// NumOfWorkers represents the number of workers used for
|
|
// concurrent copying contents of directories.
|
|
// If 0 or 1, it does not use goroutine for copying directories.
|
|
// Please refer to https://pkg.go.dev/golang.org/x/sync/semaphore for more details.
|
|
NumOfWorkers int64
|
|
|
|
// PreferConcurrent is a function to determine whether or not
|
|
// to use goroutine for copying contents of directories.
|
|
// If PreferConcurrent is nil, which is default, it does concurrent
|
|
// copying for all directories.
|
|
// If NumOfWorkers is 0 or 1, this function will be ignored.
|
|
PreferConcurrent func(srcdir, destdir string) (bool, error)
|
|
|
|
// Internal use only
|
|
intent intent
|
|
}
|
|
|
|
type intent struct {
|
|
src string
|
|
dest string
|
|
sem *semaphore.Weighted
|
|
ctx context.Context
|
|
}
|
|
|
|
// SymlinkAction represents what to do on symlink.
|
|
type SymlinkAction int
|
|
|
|
const (
|
|
// Deep creates hard-copy of contents.
|
|
Deep SymlinkAction = iota
|
|
// Shallow creates new symlink to the dest of symlink.
|
|
Shallow
|
|
// Skip does nothing with symlink.
|
|
Skip
|
|
)
|
|
|
|
// DirExistsAction represents what to do on dest dir.
|
|
type DirExistsAction int
|
|
|
|
const (
|
|
// Merge preserves or overwrites existing files under the dir (default behavior).
|
|
Merge DirExistsAction = iota
|
|
// Replace deletes all contents under the dir and copy src files.
|
|
Replace
|
|
// Untouchable does nothing for the dir, and leaves it as it is.
|
|
Untouchable
|
|
)
|
|
|
|
// getDefaultOptions provides default options,
|
|
// which would be modified by usage-side.
|
|
func getDefaultOptions(src, dest string) Options {
|
|
return Options{
|
|
OnSymlink: func(string) SymlinkAction {
|
|
return Shallow // Do shallow copy
|
|
},
|
|
OnDirExists: nil, // Default behavior is "Merge".
|
|
OnError: nil, // Default is "accept error"
|
|
Skip: nil, // Do not skip anything
|
|
AddPermission: 0, // Add nothing
|
|
PermissionControl: PerservePermission, // Just preserve permission
|
|
Sync: false, // Do not sync
|
|
Specials: false, // Do not copy special files
|
|
PreserveTimes: false, // Do not preserve the modification time
|
|
CopyBufferSize: 0, // Do not specify, use default bufsize (32*1024)
|
|
WrapReader: nil, // Do not wrap src files, use them as they are.
|
|
intent: intent{src, dest, nil, nil},
|
|
}
|
|
}
|
|
|
|
// assureOptions struct, should be called only once.
|
|
// All optional values MUST NOT BE nil/zero after assured.
|
|
func assureOptions(src, dest string, opts ...Options) Options {
|
|
defopt := getDefaultOptions(src, dest)
|
|
if len(opts) == 0 {
|
|
return defopt
|
|
}
|
|
if opts[0].OnSymlink == nil {
|
|
opts[0].OnSymlink = defopt.OnSymlink
|
|
}
|
|
if opts[0].Skip == nil {
|
|
opts[0].Skip = defopt.Skip
|
|
}
|
|
if opts[0].AddPermission > 0 {
|
|
opts[0].PermissionControl = AddPermission(opts[0].AddPermission)
|
|
} else if opts[0].PermissionControl == nil {
|
|
opts[0].PermissionControl = PerservePermission
|
|
}
|
|
opts[0].intent.src = defopt.intent.src
|
|
opts[0].intent.dest = defopt.intent.dest
|
|
return opts[0]
|
|
}
|
|
|
|
func shouldCopyDirectoryConcurrent(opt Options, srcdir, destdir string) (bool, error) {
|
|
if opt.NumOfWorkers <= 1 {
|
|
return false, nil
|
|
}
|
|
if opt.PreferConcurrent == nil {
|
|
return true, nil
|
|
}
|
|
return opt.PreferConcurrent(srcdir, destdir)
|
|
}
|