// Package aghrenameio is a wrapper around package github.com/google/renameio/v2 // that provides a similar stream-based API for both Unix and Windows systems. // While the Windows API is not technically atomic, it still provides a // consistent stream-based interface, and atomic renames of files do not seem to // be possible in all cases anyway. // // See https://github.com/google/renameio/issues/1. // // TODO(a.garipov): Consider moving to golibs/renameioutil once tried and // tested. package aghrenameio import ( "io/fs" "github.com/AdguardTeam/golibs/errors" ) // PendingFile is the interface for pending temporary files. type PendingFile interface { // Cleanup closes the file, and removes it without performing the renaming. // To close and rename the file, use CloseReplace. Cleanup() (err error) // CloseReplace closes the temporary file and replaces the destination file // with it, possibly atomically. // // This method is not safe for concurrent use by multiple goroutines. CloseReplace() (err error) // Write writes len(b) bytes from b to the File. It returns the number of // bytes written and an error, if any. Write returns a non-nil error when n // != len(b). Write(b []byte) (n int, err error) } // NewPendingFile is a wrapper around [renameio.NewPendingFile] on Unix systems // and [os.CreateTemp] on Windows. func NewPendingFile(filePath string, mode fs.FileMode) (f PendingFile, err error) { return newPendingFile(filePath, mode) } // WithDeferredCleanup is a helper that performs the necessary cleanups and // finalizations of the temporary files based on the returned error. func WithDeferredCleanup(returned error, file PendingFile) (err error) { // Make sure that any error returned from here is marked as a deferred one. if returned != nil { return errors.WithDeferred(returned, file.Cleanup()) } return errors.WithDeferred(nil, file.CloseReplace()) }