Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add FileCopyMethod option / API #164

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

eth-p
Copy link

@eth-p eth-p commented Aug 24, 2024

Implemented as discussed in #163 in this comment.

A change was needed:

I have to check for the source file being deleted after the FileCopyMethod function is run.

  • The old function returned nil if the source was deleted.
  • When called from the refactored fcopy, it would think there was no error.
  • fcopy would then try to apply changes to the destination file that was never made, then error.

I also had to change the CI so I could run workflows on my own repo. You can drop that commit before merging it.

@eth-p eth-p force-pushed the feat-copymethod-api branch 4 times, most recently from 98288f5 to 7bb84a4 Compare August 24, 2024 22:54
options.go Outdated
@@ -27,6 +27,10 @@ type Options struct {
// RenameDestination can specify the destination file or dir name if needed to rename.
RenameDestination func(src, dest string) (string, error)

// FileCopyFunc specifies the method by which a regular file is copied.
// The default is CopyBytes.
FileCopyFunc FileCopyMethod
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of using different names here?
Both the name of prop and the type can be the same. Let me know your thoughts

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I'll make them both FileCopyMethod


// ErrUnsupportedCopyMethod is returned when the FileCopyMethod specified in
// Options is not supported.
var ErrUnsupportedCopyMethod = errors.New(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the case when we use it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the user can tell if the copy failed because of no support, instead of another error like permission:

err := copy.Copy("/from", "/to", copy.Options {
    FileCopyMethod: copy.ReflinkCopy,
})

if errors.Is(err, copy.ErrUnsupportedCopyMethod) {
    // Retry with other method
    err = copy.Copy("/from", "/to", copy.Options {
        FileCopyMethod: copy.CopyBytes,
    })
}

copy.go Outdated
return
// Use FileCopyMethod to do copy.
if err = opt.FileCopyFunc.fcopy(src, dest, info, opt); err != nil {
if pathError, ok := err.(*os.PathError); ok && errors.Is(err, os.ErrNotExist) && pathError.Path == src {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just at a glance, it looks like increasing complexity of source code.
Do we really have a specific issue we need to solve in this PR?
or do you think we can separate PR for different point of interest?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I don't do this here, it breaks the file is deleted while copying test. Let me see if I can do something else though

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to have a skipFile return instead

This will be used so we can have multiple implementations for copying
a regular file. Currently, there is just CopyBytes, which is the old
fcopy code.

The FileCopyMethod struct is a struct with an un-exported member.
This is done to make the type opaque so the user can't make their
own copy function. There are still design questions left about
passing around the `Options` struct and `os.FileInfo`, so I opted
to do this so that can be figured out later.

The type can be made into `type FileCopyMethod func(...)` later,
and it won't be a breaking change. Or, instead, a
`UserCopy(func (src, dest string) error) FileCopyMethod`
function can be added.
Permission changes, ownership preservation, and access/modify/creation
time presevation is something all implementations need. I brought it
out of the CopyBytes implementation so it can be shared when more
implementations are added later.

A small note:

The "ignore if source file deleted" check had to be brought out of
`CopyBytes`. The way it worked before is it returned no error, which
meant `fcopy` would try and modify the destination file (which was never
created). Instead, I check for the error explicitly in `fcopy`.
External contributors don't do development on the main or develop
branch, but may still need to see the CI results. This lets them
do `gh workflow run Go --repo=<user>/copy` on their own branches.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants