file/object.go

102 lines
2.1 KiB
Go
Raw Normal View History

package file
import (
"bytes"
"encoding/json"
"os"
"strings"
"sync"
"apigo.cc/go/convert"
"gopkg.in/yaml.v3"
)
var fileLocksLock = sync.Mutex{}
var fileLocks = map[string]*sync.Mutex{}
func getLock(filename string) *sync.Mutex {
absName := GetAbsFilename(filename)
fileLocksLock.Lock()
defer fileLocksLock.Unlock()
if fileLocks[absName] == nil {
fileLocks[absName] = new(sync.Mutex)
}
return fileLocks[absName]
}
// UnmarshalFile 从文件读取数据并映射到 to自动处理 YAML/JSON 格式判别及智能字段映射。
func UnmarshalFile(filename string, to any) error {
data, err := ReadBytes(filename)
if err != nil {
return err
}
isYaml := strings.HasSuffix(filename, ".yml") || strings.HasSuffix(filename, ".yaml")
var in any
if isYaml {
err = yaml.Unmarshal(data, &in)
} else {
err = json.Unmarshal(data, &in)
}
if err != nil {
return err
}
convert.To(in, to)
return nil
}
// MarshalFile 将数据序列化并写入文件,支持 YAML/JSON 自动判别。
func MarshalFile(filename string, data any) error {
return marshalFile(filename, data, false)
}
// MarshalFilePretty 类似 MarshalFile但会输出带缩进的格式化内容。
func MarshalFilePretty(filename string, data any) error {
return marshalFile(filename, data, true)
}
func marshalFile(filename string, data any, indent bool) error {
isYaml := strings.HasSuffix(filename, ".yml") || strings.HasSuffix(filename, ".yaml")
var buf []byte
var err error
if isYaml {
buf, err = yaml.Marshal(data)
} else {
buffer := bytes.Buffer{}
enc := json.NewEncoder(&buffer)
enc.SetEscapeHTML(false)
if indent {
enc.SetIndent("", " ")
}
err = enc.Encode(data)
if err == nil {
buf = buffer.Bytes()
}
}
if err != nil {
return err
}
lock := getLock(filename)
lock.Lock()
defer lock.Unlock()
return WriteBytes(filename, buf)
}
func PatchFile(filename string, patch any) error {
var current map[string]any
err := UnmarshalFile(filename, &current)
if err != nil {
if os.IsNotExist(err) {
current = make(map[string]any)
} else {
return err
}
}
convert.To(patch, &current)
return MarshalFilePretty(filename, current)
}