2022-03-22 11:03:15 -04:00
|
|
|
/*
|
|
|
|
Package file provides functions that combine file handling, JSON marshaling
|
|
|
|
and file system abstraction.
|
|
|
|
*/
|
|
|
|
package file
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"io"
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
2022-04-13 03:15:27 -04:00
|
|
|
"path"
|
2022-03-22 11:03:15 -04:00
|
|
|
|
|
|
|
"github.com/spf13/afero"
|
|
|
|
)
|
|
|
|
|
2022-04-13 03:15:27 -04:00
|
|
|
// Option is a bitmask of options for file operations.
|
|
|
|
type Option uint
|
|
|
|
|
|
|
|
// Has determines if a set of options contains the given options.
|
|
|
|
func (o Option) Has(op Option) bool {
|
|
|
|
return o&op == op
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
// OptNone is a no-op.
|
|
|
|
OptNone Option = 1 << iota / 2
|
|
|
|
// OptOverwrite overwrites an existing file.
|
|
|
|
OptOverwrite
|
|
|
|
// OptMkdirAll creates the path to the file.
|
|
|
|
OptMkdirAll
|
|
|
|
)
|
|
|
|
|
2022-03-22 11:03:15 -04:00
|
|
|
// Handler handles file interaction.
|
|
|
|
type Handler struct {
|
|
|
|
fs *afero.Afero
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewHandler returns a new file handler.
|
|
|
|
func NewHandler(fs afero.Fs) Handler {
|
|
|
|
afs := &afero.Afero{Fs: fs}
|
|
|
|
return Handler{fs: afs}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read reads the file given name and returns the bytes read.
|
|
|
|
func (h *Handler) Read(name string) ([]byte, error) {
|
|
|
|
file, err := h.fs.OpenFile(name, os.O_RDONLY, 0o644)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
return io.ReadAll(file)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write writes the data bytes into the file with the given name.
|
2022-04-13 03:15:27 -04:00
|
|
|
func (h *Handler) Write(name string, data []byte, options Option) error {
|
|
|
|
if options.Has(OptMkdirAll) {
|
|
|
|
if err := h.fs.MkdirAll(path.Dir(name), os.ModePerm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2022-03-22 11:03:15 -04:00
|
|
|
flags := os.O_WRONLY | os.O_CREATE | os.O_EXCL
|
2022-04-13 03:15:27 -04:00
|
|
|
if options.Has(OptOverwrite) {
|
2022-03-22 11:03:15 -04:00
|
|
|
flags = os.O_WRONLY | os.O_CREATE | os.O_TRUNC
|
|
|
|
}
|
|
|
|
file, err := h.fs.OpenFile(name, flags, 0o644)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = file.Write(data)
|
|
|
|
if errTmp := file.Close(); errTmp != nil && err == nil {
|
|
|
|
err = errTmp
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadJSON reads a JSON file from name and unmarshals it into the content interface.
|
|
|
|
// The interface content must be a pointer to a JSON marchalable object.
|
|
|
|
func (h *Handler) ReadJSON(name string, content interface{}) error {
|
|
|
|
data, err := h.Read(name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return json.Unmarshal(data, content)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteJSON marshals the content interface to JSON and writes it to the path with the given name.
|
2022-04-13 03:15:27 -04:00
|
|
|
func (h *Handler) WriteJSON(name string, content interface{}, options Option) error {
|
2022-03-22 11:03:15 -04:00
|
|
|
jsonData, err := json.MarshalIndent(content, "", "\t")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-04-13 03:15:27 -04:00
|
|
|
return h.Write(name, jsonData, options)
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Remove deletes the file with the given name.
|
|
|
|
func (h *Handler) Remove(name string) error {
|
|
|
|
return h.fs.Remove(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stat returns a FileInfo describing the named file, or an error, if any
|
|
|
|
// happens.
|
|
|
|
func (h *Handler) Stat(name string) (fs.FileInfo, error) {
|
|
|
|
return h.fs.Stat(name)
|
|
|
|
}
|