Compare commits
3 Commits
a97e1f50bf
...
f25c7c3f92
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f25c7c3f92 | ||
|
|
121e90390b | ||
|
|
03aeb5627e |
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v1.5.5 (2026-06-21)
|
||||||
|
- **JS 对齐**: 重构 JS 导出为具名函数,并引入 `jsmod.MakeError` 动态包装错误以获取调用栈。
|
||||||
|
- **依赖更新**: 升级依赖 `jsmod` 至 `v1.5.3`,`cast` 至 `v1.5.3`,`rand` 至 `v1.5.3`,`encoding` 至 `v1.5.4`,`safe` 至 `v1.5.2`。
|
||||||
|
|
||||||
|
## v1.5.4 (2026-06-11)
|
||||||
|
- **版本对齐**: 基础设施全局对齐 v1.5.4。
|
||||||
|
|
||||||
|
## v1.5.3 (2026-06-09)
|
||||||
|
- **版本对齐**: 基础设施全局对齐 v1.5.3。
|
||||||
|
|
||||||
## v1.5.2 (2026-06-08)
|
## v1.5.2 (2026-06-08)
|
||||||
- **JS 对齐**: 将所有注册到 `jsmod` 的导出方法名统一为 PascalCase(如 `Read`, `Write`, `GetFileInfo`),消除 JS 环境下的调用摩擦。
|
- **JS 对齐**: 将所有注册到 `jsmod` 的导出方法名统一为 PascalCase(如 `Read`, `Write`, `GetFileInfo`),消除 JS 环境下的调用摩擦。
|
||||||
|
|
||||||
|
|||||||
7
TEST.md
7
TEST.md
@ -1,7 +1,7 @@
|
|||||||
# Test Report: @go/file
|
# Test Report: @go/file
|
||||||
|
|
||||||
## 📋 测试概览
|
## 📋 测试概览
|
||||||
- **测试时间**: 2026-05-06
|
- **测试时间**: 2026-06-21
|
||||||
- **测试环境**: darwin/amd64
|
- **测试环境**: darwin/amd64
|
||||||
- **Go 版本**: 1.25.0
|
- **Go 版本**: 1.25.0
|
||||||
|
|
||||||
@ -17,9 +17,10 @@
|
|||||||
## 🛡️ 鲁棒性防御 (Robustness)
|
## 🛡️ 鲁棒性防御 (Robustness)
|
||||||
- **摩擦消除**:移除 `Must` 系列 API,极大降低了业务逻辑中的错误处理心智负担。
|
- **摩擦消除**:移除 `Must` 系列 API,极大降低了业务逻辑中的错误处理心智负担。
|
||||||
- **内存虚拟化**:支持透明的内存文件系统操作,与磁盘文件 API 完全一致。
|
- **内存虚拟化**:支持透明的内存文件系统操作,与磁盘文件 API 完全一致。
|
||||||
|
- **JS 错误调用栈**:JS 桥接层改用具名导出并使用 `jsmod.MakeError` 包裹错误,确保 JS 抛出异常时携带准确的 Go 运行时堆栈。
|
||||||
|
|
||||||
## ⚡ 性能基准 (Benchmarks)
|
## ⚡ 性能基准 (Benchmarks)
|
||||||
| 函数 | 平均耗时 | 性能分析 |
|
| 函数 | 平均耗时 | 性能分析 |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
| `MarshalFile` | **115787 ns/op** | 包含 IO 写入的高性能序列化。 |
|
| `MarshalFile` | **104568 ns/op** | 包含 IO 写入的高性能序列化。 |
|
||||||
| `UnmarshalFile` | **24682 ns/op** | 高效的反序列化。 |
|
| `UnmarshalFile` | **23580 ns/op** | 高效的反序列化。 |
|
||||||
|
|||||||
10
go.mod
10
go.mod
@ -3,15 +3,15 @@ module apigo.cc/go/file
|
|||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/cast v1.5.0
|
apigo.cc/go/cast v1.5.3
|
||||||
apigo.cc/go/encoding v1.5.0
|
apigo.cc/go/encoding v1.5.4
|
||||||
apigo.cc/go/jsmod v1.5.0
|
apigo.cc/go/jsmod v1.5.3
|
||||||
apigo.cc/go/safe v1.5.0
|
apigo.cc/go/safe v1.5.2
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/rand v1.5.0 // indirect
|
apigo.cc/go/rand v1.5.3
|
||||||
github.com/kr/pretty v0.3.0 // indirect
|
github.com/kr/pretty v0.3.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||||
golang.org/x/crypto v0.52.0 // indirect
|
golang.org/x/crypto v0.52.0 // indirect
|
||||||
|
|||||||
406
js_export.go
406
js_export.go
@ -9,157 +9,273 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
jsmod.Register("file", map[string]any{
|
jsmod.Register("file", map[string]any{
|
||||||
// 读操作 (映射到私有包装器)
|
// 读操作 (映射到私有包装器)
|
||||||
"Exists": func(ctx context.Context, path string) bool {
|
"Exists": jsExists,
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
"Read": jsRead,
|
||||||
if err != nil {
|
"ReadBytes": jsReadBytes,
|
||||||
return false
|
"ReadLines": jsReadLines,
|
||||||
}
|
"ReadDir": jsReadDir,
|
||||||
return Exists(p)
|
"GetFileInfo": jsGetFileInfo,
|
||||||
},
|
|
||||||
"Read": func(ctx context.Context, path string) (string, error) {
|
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return Read(p)
|
|
||||||
},
|
|
||||||
"ReadBytes": func(ctx context.Context, path string) ([]byte, error) {
|
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ReadBytes(p)
|
|
||||||
},
|
|
||||||
"ReadLines": func(ctx context.Context, path string) ([]string, error) {
|
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ReadLines(p)
|
|
||||||
},
|
|
||||||
"ReadDir": func(ctx context.Context, path string) ([]FileInfo, error) {
|
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ReadDir(p)
|
|
||||||
},
|
|
||||||
"GetFileInfo": func(ctx context.Context, path string) *FileInfo {
|
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return GetFileInfo(p)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 写操作
|
// 写操作
|
||||||
"Write": func(ctx context.Context, path string, content string) error {
|
"Write": jsWrite,
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
"WriteBytes": jsWriteBytes,
|
||||||
if err != nil {
|
"Remove": jsRemove,
|
||||||
return err
|
"Mkdir": jsMkdir,
|
||||||
}
|
"Copy": jsCopy,
|
||||||
return Write(p, content)
|
"Move": jsMove,
|
||||||
},
|
"Replace": jsReplace,
|
||||||
"WriteBytes": func(ctx context.Context, path string, content []byte) error {
|
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return WriteBytes(p, content)
|
|
||||||
},
|
|
||||||
"Remove": func(ctx context.Context, path string) error {
|
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return Remove(p)
|
|
||||||
},
|
|
||||||
"Mkdir": func(ctx context.Context, path string) error {
|
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return Mkdir(p)
|
|
||||||
},
|
|
||||||
"Copy": func(ctx context.Context, from, to string) error {
|
|
||||||
pFrom, err := VerifyPathForSafeMode(ctx, from)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pTo, err := VerifyPathForSafeMode(ctx, to)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return Copy(pFrom, pTo)
|
|
||||||
},
|
|
||||||
"Move": func(ctx context.Context, from, to string) error {
|
|
||||||
pFrom, err := VerifyPathForSafeMode(ctx, from)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pTo, err := VerifyPathForSafeMode(ctx, to)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return Move(pFrom, pTo)
|
|
||||||
},
|
|
||||||
"Replace": func(ctx context.Context, path, old, new string) error {
|
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return Replace(p, old, new)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 序列化
|
// 序列化
|
||||||
"UnmarshalFile": func(ctx context.Context, path string, to any) error {
|
"UnmarshalFile": jsUnmarshalFile,
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
"MarshalFile": jsMarshalFile,
|
||||||
if err != nil {
|
"MarshalFilePretty": jsMarshalFilePretty,
|
||||||
return err
|
|
||||||
}
|
|
||||||
return UnmarshalFile(p, to)
|
|
||||||
},
|
|
||||||
"MarshalFile": func(ctx context.Context, path string, data any) error {
|
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return MarshalFile(p, data)
|
|
||||||
},
|
|
||||||
"MarshalFilePretty": func(ctx context.Context, path string, data any) error {
|
|
||||||
p, err := VerifyPathForSafeMode(ctx, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return MarshalFilePretty(p, data)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 归档
|
// 归档
|
||||||
"Archive": func(ctx context.Context, src, dest string) error {
|
"Archive": jsArchive,
|
||||||
pSrc, err := VerifyPathForSafeMode(ctx, src)
|
"Extract": jsExtract,
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pDest, err := VerifyPathForSafeMode(ctx, dest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return Archive(pSrc, pDest)
|
|
||||||
},
|
|
||||||
"Extract": func(ctx context.Context, src, dest string, strip bool) error {
|
|
||||||
pSrc, err := VerifyPathForSafeMode(ctx, src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pDest, err := VerifyPathForSafeMode(ctx, dest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return Extract(pSrc, pDest, strip)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 压缩工具 (无路径,不校验)
|
// 压缩工具
|
||||||
"Compress": Compress,
|
"Compress": jsCompress,
|
||||||
"Decompress": Decompress,
|
"Decompress": jsDecompress,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func jsExists(ctx context.Context, path string) bool {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return Exists(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsRead(ctx context.Context, path string) (string, error) {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return "", jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
res, err := Read(p)
|
||||||
|
if err != nil {
|
||||||
|
return "", jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsReadBytes(ctx context.Context, path string) ([]byte, error) {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
res, err := ReadBytes(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsReadLines(ctx context.Context, path string) ([]string, error) {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
res, err := ReadLines(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsReadDir(ctx context.Context, path string) ([]FileInfo, error) {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
res, err := ReadDir(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsGetFileInfo(ctx context.Context, path string) *FileInfo {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return GetFileInfo(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsWrite(ctx context.Context, path string, content string) error {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = Write(p, content)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsWriteBytes(ctx context.Context, path string, content []byte) error {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = WriteBytes(p, content)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsRemove(ctx context.Context, path string) error {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = Remove(p)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsMkdir(ctx context.Context, path string) error {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = Mkdir(p)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsCopy(ctx context.Context, from, to string) error {
|
||||||
|
pFrom, err := VerifyPathForSafeMode(ctx, from)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
pTo, err := VerifyPathForSafeMode(ctx, to)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = Copy(pFrom, pTo)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsMove(ctx context.Context, from, to string) error {
|
||||||
|
pFrom, err := VerifyPathForSafeMode(ctx, from)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
pTo, err := VerifyPathForSafeMode(ctx, to)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = Move(pFrom, pTo)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsReplace(ctx context.Context, path, old, new string) error {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = Replace(p, old, new)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsUnmarshalFile(ctx context.Context, path string, to any) error {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = UnmarshalFile(p, to)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsMarshalFile(ctx context.Context, path string, data any) error {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = MarshalFile(p, data)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsMarshalFilePretty(ctx context.Context, path string, data any) error {
|
||||||
|
p, err := VerifyPathForSafeMode(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = MarshalFilePretty(p, data)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsArchive(ctx context.Context, src, dest string) error {
|
||||||
|
pSrc, err := VerifyPathForSafeMode(ctx, src)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
pDest, err := VerifyPathForSafeMode(ctx, dest)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = Archive(pSrc, pDest)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsExtract(ctx context.Context, src, dest string, strip bool) error {
|
||||||
|
pSrc, err := VerifyPathForSafeMode(ctx, src)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
pDest, err := VerifyPathForSafeMode(ctx, dest)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
err = Extract(pSrc, pDest, strip)
|
||||||
|
if err != nil {
|
||||||
|
return jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsCompress(data []byte, cType string) ([]byte, error) {
|
||||||
|
res, err := Compress(data, cType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsDecompress(data []byte, cType string) ([]byte, error) {
|
||||||
|
res, err := Decompress(data, cType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, jsmod.MakeError(err)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user