102 lines
2.1 KiB
Go
102 lines
2.1 KiB
Go
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, ¤t)
|
||
if err != nil {
|
||
if os.IsNotExist(err) {
|
||
current = make(map[string]any)
|
||
} else {
|
||
return err
|
||
}
|
||
}
|
||
convert.To(patch, ¤t)
|
||
return MarshalFilePretty(filename, current)
|
||
}
|