file/object.go

102 lines
2.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}