-
-
Notifications
You must be signed in to change notification settings - Fork 115
/
options.go
193 lines (163 loc) · 6.12 KB
/
options.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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)
// RenameDestination can specify the destination file or dir name if needed to rename.
RenameDestination func(src, dest string) (string, error)
// FileCopyMethod specifies the method by which a regular file is copied.
// The default is CopyBytes.
//
// Available implementations:
// - CopyBytes (best compatibility)
//
// Some implementations may not be supported on the target GOOS, or on
// the user's filesystem. When these fail, an error will be returned.
FileCopyMethod FileCopyMethod
// 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
)
// FileCopyMethod represents one of the ways that a regular file can be copied.
type FileCopyMethod struct {
fcopy func(src, dest string, info os.FileInfo, opt Options) (err error, skipFile bool)
}
// 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
FileCopyMethod: CopyBytes, // Copy by bytes
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
}
if opts[0].FileCopyMethod.fcopy == nil {
opts[0].FileCopyMethod = defopt.FileCopyMethod
}
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)
}