Bring the Wails desktop shell to feature parity with the CLI for provider and alias management while keeping the browser fallback working. Wire tray, notifications, autostart, and GUI warnings so desktop flows behave predictably.
84 lines
1.8 KiB
Go
84 lines
1.8 KiB
Go
package fileutil
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
type LockedFile struct {
|
|
file *os.File
|
|
}
|
|
|
|
func WithLockedFile(path string, fn func() error) error {
|
|
lock, err := acquireLock(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer lock.Close()
|
|
return fn()
|
|
}
|
|
|
|
func AtomicWriteFile(path string, data []byte, perm os.FileMode) error {
|
|
dir := filepath.Dir(path)
|
|
base := filepath.Base(path)
|
|
tmp, err := os.CreateTemp(dir, base+".*.tmp")
|
|
if err != nil {
|
|
return fmt.Errorf("create tmp: %w", err)
|
|
}
|
|
tmpPath := tmp.Name()
|
|
defer func() {
|
|
_ = os.Remove(tmpPath)
|
|
}()
|
|
if err := tmp.Chmod(perm); err != nil {
|
|
_ = tmp.Close()
|
|
return fmt.Errorf("chmod tmp: %w", err)
|
|
}
|
|
if _, err := tmp.Write(data); err != nil {
|
|
_ = tmp.Close()
|
|
return fmt.Errorf("write tmp: %w", err)
|
|
}
|
|
if err := tmp.Sync(); err != nil {
|
|
_ = tmp.Close()
|
|
return fmt.Errorf("sync tmp: %w", err)
|
|
}
|
|
if err := tmp.Close(); err != nil {
|
|
return fmt.Errorf("close tmp: %w", err)
|
|
}
|
|
if err := os.Rename(tmpPath, path); err != nil {
|
|
return fmt.Errorf("rename tmp: %w", err)
|
|
}
|
|
if err := syncDir(dir); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func acquireLock(path string) (*LockedFile, error) {
|
|
lockPath := path + ".lock"
|
|
file, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0o600)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open lock: %w", err)
|
|
}
|
|
if err := lockFile(file); err != nil {
|
|
_ = file.Close()
|
|
return nil, fmt.Errorf("lock file: %w", err)
|
|
}
|
|
return &LockedFile{file: file}, nil
|
|
}
|
|
|
|
func (l *LockedFile) Close() error {
|
|
if l == nil || l.file == nil {
|
|
return nil
|
|
}
|
|
unlockErr := unlockFile(l.file)
|
|
closeErr := l.file.Close()
|
|
if unlockErr != nil {
|
|
return fmt.Errorf("unlock file: %w", unlockErr)
|
|
}
|
|
if closeErr != nil {
|
|
return fmt.Errorf("close lock: %w", closeErr)
|
|
}
|
|
return nil
|
|
}
|