chore: 优化目录处理与命令执行,对齐基础设施 (by AI)

This commit is contained in:
AI Engineer 2026-05-05 17:46:27 +08:00
parent 916226a9df
commit 4f750c8acc
7 changed files with 75 additions and 27 deletions

View File

@ -1,10 +1,13 @@
# Changelog # Changelog
## [1.0.4] - 2026-05-01 ## [1.0.5] - 2026-05-05
- 版本对齐至 v1.0.4。 - **性能优化**: 优化 `EnsureParentDir`,减少冗余的系统调用。
- 移除 AI.md将其内容整合至 README.md提升模块文档规范。 - **基础设施对齐**: `UnmarshalFile` 迁移至 `cast.To` 语义对齐(内部仍使用 `Convert` 确保指针更新)。
- 进一步优化文件系统 API 交互细节。 - **健壮性**: `RunCommand` 现在支持过滤空行,并提供更详细的执行错误信息。
- **命名规范**: `EnsureDirSuffix` 内部变量 `spe` 重命名为 `sep`
- **测试增强**: 补全了 `Marshal/Unmarshal` 的 Benchmark 测试。
## [1.0.4] - 2026-05-01
...
## [1.0.0] - 2026-05-01
- 初始版本归档与模块对齐。
- 修复了依赖引用问题convert 模块函数命名不一致导致编译失败)。 - 修复了依赖引用问题convert 模块函数命名不一致导致编译失败)。

View File

@ -5,7 +5,7 @@
## 核心特性 ## 核心特性
* **内存文件系统 (Memory File System)**: 支持将文件或资源加载到内存,并可选进行压缩与 `SafeBuf` 加密存储。这非常适合处理嵌入式资源、敏感配置或高并发读取的场景。 * **内存文件系统 (Memory File System)**: 支持将文件或资源加载到内存,并可选进行压缩与 `SafeBuf` 加密存储。这非常适合处理嵌入式资源、敏感配置或高并发读取的场景。
* **智能序列化**: 自动处理 YAML 与 JSON 格式判别,并集成了强大的 `convert.To` 智能字段映射引擎,轻松解决复杂结构体与配置文件之间的映射问题。 * **智能序列化**: 自动处理 YAML 与 JSON 格式判别,并集成了强大的 `cast.To` 智能字段映射引擎,轻松解决复杂结构体与配置文件之间的映射问题。
* **安全闭环**: 原生支持敏感数据安全缓存 (`SafeBuf`),结合内存零填充 (`ZeroMemory`),极大降低了密钥等敏感信息在内存中泄露的风险。 * **安全闭环**: 原生支持敏感数据安全缓存 (`SafeBuf`),结合内存零填充 (`ZeroMemory`),极大降低了密钥等敏感信息在内存中泄露的风险。
* **全功能 IO**: 提供从基础 IO 到递归目录操作、文件原地替换及高效压缩归档的一站式解决方案。 * **全功能 IO**: 提供从基础 IO 到递归目录操作、文件原地替换及高效压缩归档的一站式解决方案。
@ -13,7 +13,7 @@
1. **内存安全优先**: 处理敏感文件时,优先使用 `SafeLoad` 系列,并通过 `SafeBuf` 接口操作。 1. **内存安全优先**: 处理敏感文件时,优先使用 `SafeLoad` 系列,并通过 `SafeBuf` 接口操作。
2. **高性能 IO**: 在文件 IO 高并发场景下,默认采用 `WriteBytes` 以保证写入性能。 2. **高性能 IO**: 在文件 IO 高并发场景下,默认采用 `WriteBytes` 以保证写入性能。
3. **语义一致性**: 序列化/反序列化(`Unmarshal/Marshal`)通过 `convert.To` 处理自动映射,无需关心字段名不一致问题。 3. **语义一致性**: 序列化/反序列化(`Unmarshal/Marshal`)通过 `cast.To` 处理自动映射,无需关心字段名不一致问题。
4. **资源管理**: 任何归档提取或 IO 流操作结束后,必须确保对应资源被 `Close()` 4. **资源管理**: 任何归档提取或 IO 流操作结束后,必须确保对应资源被 `Close()`
## 快速入门 (Quick Start) ## 快速入门 (Quick Start)

View File

@ -30,10 +30,9 @@
| Benchmark | ops | 耗时 (ns/op) | | Benchmark | ops | 耗时 (ns/op) |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| MarshalFile/Normal | 10000 | 111,770 | | MarshalFile/Normal | 10000 | 101,058 |
| MarshalFile/Pretty | 10000 | 109,581 | | MarshalFile/Pretty | 10000 | 105,661 |
| UnmarshalFile/Normal | 52528 | 22,172 | | UnmarshalFile | 46378 | 25,102 |
| UnmarshalFile/X | 46568 | 24,833 |
## 3. 系统鲁棒性测试 ## 3. 系统鲁棒性测试
- **不存在的文件**: 验证 `UnmarshalFile` 返回 `os.IsNotExist` 类型错误。 - **不存在的文件**: 验证 `UnmarshalFile` 返回 `os.IsNotExist` 类型错误。

40
bench_test.go Normal file
View File

@ -0,0 +1,40 @@
package file
import (
"os"
"path/filepath"
"testing"
)
func BenchmarkMarshalFile(b *testing.B) {
tmpDir, _ := os.MkdirTemp("", "bench_marshal")
defer os.RemoveAll(tmpDir)
filename := filepath.Join(tmpDir, "test.json")
data := TestStruct{Name: "Benchmark", Age: 100}
b.Run("Normal", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = MarshalFile(filename, data)
}
})
b.Run("Pretty", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = MarshalFilePretty(filename, data)
}
})
}
func BenchmarkUnmarshalFile(b *testing.B) {
tmpDir, _ := os.MkdirTemp("", "bench_unmarshal")
defer os.RemoveAll(tmpDir)
filename := filepath.Join(tmpDir, "test.json")
data := `{"name": "Benchmark", "age": 100}`
os.WriteFile(filename, []byte(data), 0644)
var ts TestStruct
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = UnmarshalFile(filename, &ts)
}
}

22
file.go
View File

@ -2,6 +2,7 @@ package file
import ( import (
"bufio" "bufio"
"fmt"
"io" "io"
"os" "os"
"os/exec" "os/exec"
@ -23,16 +24,13 @@ func EnsureParentDir(filename string) {
if pos < 0 { if pos < 0 {
return return
} }
path := filename[0:pos] _ = os.MkdirAll(filename[:pos], 0755)
if _, err := os.Stat(path); err != nil {
_ = os.MkdirAll(path, 0755)
}
} }
func EnsureDirSuffix(path string) string { func EnsureDirSuffix(path string) string {
const spe = string(os.PathSeparator) const sep = string(os.PathSeparator)
if !strings.HasSuffix(path, spe) { if !strings.HasSuffix(path, sep) {
return path + spe return path + sep
} }
return path return path
} }
@ -266,8 +264,14 @@ func RunCommand(command string, args ...string) ([]string, error) {
cmd := exec.Command(command, args...) cmd := exec.Command(command, args...)
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("command execution failed: %w, output: %s", err, string(out))
}
rawLines := strings.Split(strings.TrimSpace(string(out)), "\n")
var lines []string
for _, line := range rawLines {
if trimmed := strings.TrimSpace(line); trimmed != "" {
lines = append(lines, trimmed)
}
} }
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
return lines, nil return lines, nil
} }

6
go.mod
View File

@ -3,15 +3,17 @@ module apigo.cc/go/file
go 1.25.0 go 1.25.0
require ( require (
apigo.cc/go/convert v1.0.4 apigo.cc/go/cast v1.2.6
apigo.cc/go/encoding v1.0.4 apigo.cc/go/encoding v1.0.4
apigo.cc/go/safe v1.0.4 apigo.cc/go/safe v1.0.4
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
apigo.cc/go/cast v1.0.4 // indirect
apigo.cc/go/rand v1.0.4 // indirect apigo.cc/go/rand v1.0.4 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
golang.org/x/crypto v0.50.0 // indirect golang.org/x/crypto v0.50.0 // indirect
golang.org/x/sys v0.43.0 // indirect golang.org/x/sys v0.43.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
) )

View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
"sync" "sync"
"apigo.cc/go/convert" "apigo.cc/go/cast"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@ -42,7 +42,7 @@ func UnmarshalFile(filename string, to any) error {
return err return err
} }
convert.To(in, to) cast.Convert(to, in)
return nil return nil
} }
@ -96,6 +96,6 @@ func PatchFile(filename string, patch any) error {
return err return err
} }
} }
convert.To(patch, &current) cast.Convert(&current, patch)
return MarshalFilePretty(filename, current) return MarshalFilePretty(filename, current)
} }