From 1f9d169cf1506ce518728689295d88986675f23c Mon Sep 17 00:00:00 2001 From: xiaoqidun Date: Wed, 19 Aug 2026 14:27:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=A0=BC=E5=BC=8F=E9=85=8D=E7=BD=AE):=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=A0=BC=E5=BC=8F=E5=8C=96=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- goini.go | 68 ++++++++++++++++++++++++++++++++++++++++++++--------- internal.go | 43 +++++++++++++++++++++++++++++---- 2 files changed, 96 insertions(+), 15 deletions(-) diff --git a/goini.go b/goini.go index e81818d..510f217 100644 --- a/goini.go +++ b/goini.go @@ -21,7 +21,6 @@ var ( // GoINI 表示INI配置解析器 type GoINI struct { - data []byte filename string fileLines []iniLine structTag string @@ -43,8 +42,7 @@ func NewGoINI() *GoINI { // 入参: fileData 配置文件内容 func (ini *GoINI) SetData(fileData []byte) { ini.filename = "" - ini.data = append(ini.data[:0], fileData...) - ini.parse() + ini.parse(string(fileData)) } // LoadFile 从文件读取配置并解析 @@ -56,28 +54,76 @@ func (ini *GoINI) LoadFile(filename string) error { return err } ini.filename = filename - ini.data = append(ini.data[:0], b...) - ini.parse() + ini.parse(string(b)) return nil } // Save 将配置写回LoadFile读取的文件 // 返回: error 错误信息 func (ini *GoINI) Save() error { - if ini.filename == "" { - return errFilenameRequired + return ini.save(ini.String()) +} + +// SaveFormat 将配置格式化并写回LoadFile读取的文件 +// 返回: error 错误信息 +func (ini *GoINI) SaveFormat() error { + formatted := ini.formatString() + if err := ini.save(formatted); err != nil { + return err } - return ini.SaveFile(ini.filename) + ini.parse(formatted) + return nil } // SaveFile 将配置写入指定文件 -// 入参: filename 配置文件路径 +// 入参: filename 配置文件路径, perm 文件权限 // 返回: error 错误信息 -func (ini *GoINI) SaveFile(filename string) error { +func (ini *GoINI) SaveFile(filename string, perm os.FileMode) error { + return ini.saveFile(filename, ini.String(), perm) +} + +// SaveFormatFile 将配置格式化并写入指定文件 +// 入参: filename 配置文件路径, perm 文件权限 +// 返回: error 错误信息 +func (ini *GoINI) SaveFormatFile(filename string, perm os.FileMode) error { + formatted := ini.formatString() + if err := ini.saveFile(filename, formatted, perm); err != nil { + return err + } + ini.parse(formatted) + return nil +} + +func (ini *GoINI) save(data string) error { + if ini.filename == "" { + return errFilenameRequired + } + info, err := os.Stat(ini.filename) + if err != nil { + return err + } + return ini.saveFile(ini.filename, data, info.Mode().Perm()) +} + +func (ini *GoINI) saveFile(filename string, data string, perm os.FileMode) error { if filename == "" { return errFilenameRequired } - if err := os.WriteFile(filename, []byte(ini.String()), 0o666); err != nil { + perm = perm.Perm() + writePerm := perm | 0o200 + if info, err := os.Stat(filename); err == nil { + if info.Mode().IsRegular() { + if err := os.Chmod(filename, writePerm); err != nil { + return err + } + } + } else if !errors.Is(err, os.ErrNotExist) { + return err + } + if err := os.WriteFile(filename, []byte(data), writePerm); err != nil { + return err + } + if err := os.Chmod(filename, perm); err != nil { return err } ini.filename = filename diff --git a/internal.go b/internal.go index 9c857dd..2223523 100644 --- a/internal.go +++ b/internal.go @@ -36,14 +36,14 @@ type parsedKeyValue struct { separator string } -func (ini *GoINI) parse() { +func (ini *GoINI) parse(data string) { ini.fileLines = nil currentSection := ini.rootSection - if len(ini.data) == 0 { + if data == "" { ini.rebuildIndexes() return } - for _, rawLine := range strings.Split(string(ini.data), "\n") { + for _, rawLine := range strings.Split(data, "\n") { line, section := parseRawLine(rawLine, currentSection) currentSection = section ini.fileLines = append(ini.fileLines, line) @@ -187,7 +187,7 @@ func (ini *GoINI) addSectionKey(sectionName string, keyName string, keyValue str func (ini *GoINI) ensureParsed() { if ini.sectionValues == nil { - ini.parse() + ini.parse("") } } @@ -358,6 +358,41 @@ func renderLine(line iniLine) string { } } +func (ini *GoINI) formatString() string { + formattedLines := make([]string, 0, len(ini.fileLines)) + pendingComments := make([]string, 0) + for _, line := range ini.fileLines { + switch line.kind { + case lineSection: + if len(formattedLines) > 0 { + formattedLines = append(formattedLines, "") + } + formattedLines = append(formattedLines, pendingComments...) + pendingComments = pendingComments[:0] + formattedLines = append(formattedLines, "["+line.sectionName+"]") + case lineKeyValue: + formattedLines = append(formattedLines, pendingComments...) + pendingComments = pendingComments[:0] + formattedLines = append(formattedLines, line.keyName+defaultSeparator+formatValue(line.keyValue)) + case lineComment: + pendingComments = append(pendingComments, formatINIComment(line.text)) + } + } + formattedLines = append(formattedLines, pendingComments...) + if len(formattedLines) == 0 { + return "" + } + return strings.Join(formattedLines, "\n") + "\n" +} + +func formatINIComment(comment string) string { + comment = stripComment(comment) + if comment == "" { + return "#" + } + return "# " + comment +} + func renderKeyValue(line iniLine) string { separator := line.separator if separator == "" {