safewrite

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 28, 2026 License: MIT Imports: 6 Imported by: 2

README

go-safewrite

( English / Japanese )

go-safewrite is a wrapper around *os.File for safely updating and replacing files, mainly intended for editors and similar applications.

package safewrite // import "github.com/hymkor/go-safewrite"

func Open(
    name string,
    confirmOverwrite func(*Info) bool,
) (File, error)
  • When opening a file such as foo, a temporary file like foo.tmp-* is created, and the original file is replaced on Close.
  • If a file with the same name already exists, a callback function is invoked to decide whether to overwrite or cancel.
  • The original file is backed up using the name foo~.
  • To avoid making backups meaningless due to frequent saves, only the oldest backup is kept within the same process.
  • Read-only files can also be replaced, depending on the decision made by the callback function.
    • File permissions are cleared on Close, but can be restored later using RestorePerm.
  • Open behaves the same as os.Create when the target file does not exist or when the target is a device file.

Example

package main

import (
    "bufio"
    "errors"
    "fmt"
    "os"
    "strings"

    "github.com/hymkor/go-safewrite"
)

func mains() error {
    prompt := func(info *safewrite.Info) bool {
        sc := bufio.NewScanner(os.Stdin)
        for {
            if info.ReadOnly() {
                fmt.Printf("Overwrite READONLY file %q ? ", info.Name)
            } else {
                fmt.Printf("Overwrite file %q ? ", info.Name)
            }
            if !sc.Scan() {
                return false
            }
            ans := sc.Text()
            if strings.EqualFold(ans, "y") {
                return true
            }
            if strings.EqualFold(ans, "n") {
                return false
            }
        }
    }
    fd, err := safewrite.Open("sample.out", prompt)
    if err != nil {
        return err
    }
    fmt.Fprintln(fd, "sample output.")
    err = fd.Close()
    if err != nil {
        return err
    }
    return safewrite.RestorePerm(fd)
}

func main() {
    if err := mains(); err != nil {
        fmt.Fprintln(os.Stderr, err.Error())

        var e safewrite.WorkingFileError
        if errors.As(err, &e) {
            fmt.Fprintln(os.Stderr, "Working file left at:", e.WorkingFile())
        }

        os.Exit(1)
    }
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrOverWriteRejected = errors.New("overwrite rejected")
)

Functions

func RestorePerm added in v0.3.0

func RestorePerm(wc File) error

RestorePerm restores the file permissions of a file that was replaced by safewrite.Open and finalized by Close.

This function is intended to be called explicitly *after* Close, using the value returned from Open as its argument.

safewrite does not automatically restore permissions during Close, because changing permissions immediately may cause subsequent overwrite operations within the same process to fail (for example, when a file becomes read-only after the first save).

By requiring an explicit call, applications can control when and whether the original permissions should be restored, such as once at process exit.

Types

type BackupError added in v0.2.0

type BackupError struct {
	Target string
	Backup string
	Err    error

	// Tmp is the path to a temporary file that may be left on disk
	// when the backup operation fails.
	// It is provided for diagnostic or recovery purposes and does not
	// affect the error condition itself.
	Tmp string
}

BackupError reports a failure that occurred while creating or replacing a backup file before overwriting the target file.

The error indicates that the original file was not replaced. In this case, a temporary file may remain on disk.

func (*BackupError) Error added in v0.2.0

func (e *BackupError) Error() string

func (*BackupError) Unwrap added in v0.2.0

func (e *BackupError) Unwrap() error

func (*BackupError) WorkingFile added in v0.2.0

func (e *BackupError) WorkingFile() string

type File added in v0.4.0

type File interface {
	io.Writer
	io.Closer
	io.Seeker
	Name() string
}

func Open

func Open(
	name string,
	confirmOverwrite func(*Info) bool,
) (File, error)

type Info

type Info struct {
	Name   string
	Mode   fs.FileMode
	Status Status
}

func (Info) ReadOnly

func (i Info) ReadOnly() bool

type ReplaceError added in v0.2.0

type ReplaceError struct {
	Tmp    string
	Target string
	Err    error
}

ReplaceError is returned when replacing the target file with a temporary file fails during a safe overwrite operation.

It typically wraps an underlying *os.LinkError or filesystem-related error.

func (*ReplaceError) Error added in v0.2.0

func (e *ReplaceError) Error() string

func (*ReplaceError) Unwrap added in v0.2.0

func (e *ReplaceError) Unwrap() error

func (*ReplaceError) WorkingFile added in v0.2.0

func (e *ReplaceError) WorkingFile() string

type Status added in v0.3.0

type Status int

Status represents how a target file has been handled by safewrite within the current process.

It does NOT describe the state of the file on disk. Instead, it records the history of how the file was opened or replaced by safewrite during the lifetime of this process.

This information is primarily intended to help applications decide whether to prompt the user again for overwrite confirmation.

const (
	// NONE indicates that, within the current process, safewrite has not yet
	// written to this file.
	//
	// This means there is no prior record of the file being created or
	// overwritten by safewrite in this process.
	NONE Status = iota

	// CREATE indicates that, within the current process, the file was previously
	// created using os.Create via safewrite.Open.
	//
	// In other words, the file did not exist at the time of the first write
	// performed by this process.
	CREATE

	// OVERWRITE indicates that, within the current process, an existing regular
	// file was previously replaced using a temporary file created by
	// os.CreateTemp and then renamed into place.
	//
	// This means safewrite has already performed a safe overwrite operation
	// for this file in this process.
	OVERWRITE
)

type WorkingFileError added in v0.2.0

type WorkingFileError interface {
	error
	WorkingFile() string
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL