mirror of
https://github.com/xiaoqidun/goini.git
synced 2026-07-12 20:58:26 +08:00
feat(支持写入): 重构代码并支持写入
This commit is contained in:
-10
@@ -1,10 +0,0 @@
|
|||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: default
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: build
|
|
||||||
pull: if-not-exists
|
|
||||||
image: golang
|
|
||||||
commands:
|
|
||||||
- go build goini.go
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2018 xiaoqidun@gmail.com
|
Copyright (c) 2018-2026 肖其顿 (XIAO QI DUN)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GoINI [](https://pkg.go.dev/github.com/xiaoqidun/goini)
|
# GoINI [](https://pkg.go.dev/github.com/xiaoqidun/goini)
|
||||||
|
|
||||||
简单易用的Golang INI配置解析库
|
简单易用、零依赖的 Go 语言 INI 配置文件读写库
|
||||||
|
|
||||||
# 安装方法
|
# 安装方法
|
||||||
|
|
||||||
|
|||||||
@@ -1,185 +1,207 @@
|
|||||||
package goini
|
package goini
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GoINI GoINI数据结构
|
const (
|
||||||
|
defaultTag = "goini"
|
||||||
|
defaultSeparator = " = "
|
||||||
|
defaultRootSection = "BuiltCommon"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errFilenameRequired = errors.New("filename required")
|
||||||
|
errStructPointerRequired = errors.New("struct pointer required")
|
||||||
|
)
|
||||||
|
|
||||||
|
// GoINI 表示INI配置解析器
|
||||||
type GoINI struct {
|
type GoINI struct {
|
||||||
tag string
|
|
||||||
data []byte
|
data []byte
|
||||||
dataMap map[string]map[string]string
|
filename string
|
||||||
nameList []string
|
fileLines []iniLine
|
||||||
dataKeyList map[string][]string
|
structTag string
|
||||||
commonField string
|
rootSection string
|
||||||
|
sectionKeys map[string][]string
|
||||||
|
sectionOrder []string
|
||||||
|
sectionValues map[string]map[string]string
|
||||||
|
sectionLineIndexes map[string]int
|
||||||
|
sectionKeyLineIndexes map[string]map[string]int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGoINI 获取GoINI对象
|
// NewGoINI 创建GoINI对象
|
||||||
|
// 返回: *GoINI GoINI对象
|
||||||
func NewGoINI() *GoINI {
|
func NewGoINI() *GoINI {
|
||||||
return &GoINI{
|
return &GoINI{structTag: defaultTag, rootSection: defaultRootSection}
|
||||||
tag: "goini",
|
|
||||||
commonField: "BuiltCommon",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse ini配置文件解析器
|
// SetData 从字节内容读取配置并解析
|
||||||
func (ini *GoINI) parse() {
|
// 入参: fileData 配置文件内容
|
||||||
currentName := ini.commonField
|
|
||||||
iniData := make(map[string]map[string]string)
|
|
||||||
iniData[currentName] = make(map[string]string)
|
|
||||||
ini.nameList = append(ini.nameList, currentName)
|
|
||||||
ini.dataKeyList = make(map[string][]string)
|
|
||||||
for _, v := range bytes.Split(ini.data, []byte("\n")) {
|
|
||||||
line := bytes.TrimSpace(v)
|
|
||||||
lineLen := len(line)
|
|
||||||
if line == nil || lineLen < 3 ||
|
|
||||||
line[0] == 35 || line[0] == 59 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if line[0] == 91 && line[lineLen-1] == 93 {
|
|
||||||
name := bytes.TrimSpace(line[1 : lineLen-1])
|
|
||||||
if name != nil {
|
|
||||||
currentName = string(name)
|
|
||||||
if _, ok := iniData[currentName]; !ok {
|
|
||||||
iniData[currentName] = make(map[string]string)
|
|
||||||
ini.nameList = append(ini.nameList, currentName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
split := bytes.IndexByte(line, 61)
|
|
||||||
if split == -1 {
|
|
||||||
split = bytes.IndexByte(line, 32)
|
|
||||||
if split == -1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
key := bytes.TrimSpace(line[0:split])
|
|
||||||
value := bytes.TrimSpace(line[split+1:])
|
|
||||||
valueLen := len(value)
|
|
||||||
keyStr, valueStr := string(key), string(value)
|
|
||||||
if keyStr == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := iniData[currentName][keyStr]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ini.dataKeyList[currentName] = append(ini.dataKeyList[currentName], keyStr)
|
|
||||||
if value == nil {
|
|
||||||
iniData[currentName][keyStr] = ""
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if valueLen >= 2 &&
|
|
||||||
((value[0] == 34 && value[valueLen-1] == 34) ||
|
|
||||||
(value[0] == 39 && value[valueLen-1] == 39) ||
|
|
||||||
(value[0] == 96 && value[valueLen-1] == 96)) {
|
|
||||||
iniData[currentName][keyStr] = string(value[1 : valueLen-1])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
iniData[currentName][keyStr] = valueStr
|
|
||||||
}
|
|
||||||
ini.dataMap = iniData
|
|
||||||
}
|
|
||||||
|
|
||||||
// String 获取GoINI对象字符串形式
|
|
||||||
func (ini *GoINI) String() string {
|
|
||||||
var iniLines []string
|
|
||||||
for _, name := range ini.GetNames("") {
|
|
||||||
if name != ini.commonField {
|
|
||||||
if len(iniLines) < 1 {
|
|
||||||
iniLines = append(iniLines, "["+name+"]")
|
|
||||||
} else {
|
|
||||||
iniLines = append(iniLines, "\n["+name+"]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, key := range ini.GetNameKeys(name, "") {
|
|
||||||
nameValue := ini.GetString(name, key, "")
|
|
||||||
if ok, _ := regexp.MatchString("^\\s|\\s$", nameValue); ok {
|
|
||||||
iniLines = append(iniLines, key+" = \""+nameValue+"\"")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
nameValueLen := len(nameValue)
|
|
||||||
if nameValueLen >= 2 &&
|
|
||||||
((nameValue[0] == 34 && nameValue[nameValueLen-1] == 34) ||
|
|
||||||
(nameValue[0] == 39 && nameValue[nameValueLen-1] == 39) ||
|
|
||||||
(nameValue[0] == 96 && nameValue[nameValueLen-1] == 96)) {
|
|
||||||
tag := `"`
|
|
||||||
if nameValue[0] == 34 {
|
|
||||||
tag = "`"
|
|
||||||
}
|
|
||||||
iniLines = append(iniLines, key+" = "+tag+nameValue+tag)
|
|
||||||
} else {
|
|
||||||
iniLines = append(iniLines, key+" = "+nameValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(iniLines, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTag 设置结构体的tag键名称
|
|
||||||
func (ini *GoINI) SetTag(tag string) {
|
|
||||||
ini.tag = tag
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetData 从代码读取配置并解析
|
|
||||||
func (ini *GoINI) SetData(fileData []byte) {
|
func (ini *GoINI) SetData(fileData []byte) {
|
||||||
ini.data = bytes.TrimSpace(fileData)
|
ini.filename = ""
|
||||||
|
ini.data = append(ini.data[:0], fileData...)
|
||||||
ini.parse()
|
ini.parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadFile 从文件读取配置并解析
|
// LoadFile 从文件读取配置并解析
|
||||||
func (ini *GoINI) LoadFile(fileName string) error {
|
// 入参: filename 配置文件路径
|
||||||
b, err := os.ReadFile(fileName)
|
// 返回: error 错误信息
|
||||||
|
func (ini *GoINI) LoadFile(filename string) error {
|
||||||
|
b, err := os.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ini.data = bytes.TrimSpace(b)
|
ini.filename = filename
|
||||||
|
ini.data = append(ini.data[:0], b...)
|
||||||
ini.parse()
|
ini.parse()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save 将配置写回LoadFile读取的文件
|
||||||
|
// 返回: error 错误信息
|
||||||
|
func (ini *GoINI) Save() error {
|
||||||
|
if ini.filename == "" {
|
||||||
|
return errFilenameRequired
|
||||||
|
}
|
||||||
|
return ini.SaveFile(ini.filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveFile 将配置写入指定文件
|
||||||
|
// 入参: filename 配置文件路径
|
||||||
|
// 返回: error 错误信息
|
||||||
|
func (ini *GoINI) SaveFile(filename string) error {
|
||||||
|
if filename == "" {
|
||||||
|
return errFilenameRequired
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filename, []byte(ini.String()), 0o666); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ini.filename = filename
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String 获取GoINI对象的字符串形式
|
||||||
|
// 返回: string 配置文件内容
|
||||||
|
func (ini *GoINI) String() string {
|
||||||
|
if len(ini.fileLines) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
renderedLines := make([]string, len(ini.fileLines))
|
||||||
|
for i, line := range ini.fileLines {
|
||||||
|
renderedLines[i] = renderLine(line)
|
||||||
|
}
|
||||||
|
return strings.Join(renderedLines, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetString 设置单个配置项的字符串值
|
||||||
|
// 入参: name 分区名称, key 配置项名称, value 配置项值
|
||||||
|
func (ini *GoINI) SetString(name string, key string, value string) {
|
||||||
|
ini.ensureParsed()
|
||||||
|
name = ini.normalizeSection(name)
|
||||||
|
key = strings.TrimSpace(key)
|
||||||
|
if key == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if index, ok := ini.findSectionKeyLineIndex(name, key); ok {
|
||||||
|
ini.fileLines[index].keyValue = value
|
||||||
|
ini.fileLines[index].modified = true
|
||||||
|
ini.rebuildIndexes()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ini.ensureSectionLine(name)
|
||||||
|
ini.insertLine(ini.findSectionInsertIndex(name), iniLine{kind: lineKeyValue, sectionName: name, keyName: key, keyValue: value, separator: defaultSeparator, modified: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBool 设置单个配置项的布尔值
|
||||||
|
// 入参: name 分区名称, key 配置项名称, value 配置项值
|
||||||
|
func (ini *GoINI) SetBool(name string, key string, value bool) {
|
||||||
|
ini.SetString(name, key, strconv.FormatBool(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInt64 设置单个配置项的整数值
|
||||||
|
// 入参: name 分区名称, key 配置项名称, value 配置项值
|
||||||
|
func (ini *GoINI) SetInt64(name string, key string, value int64) {
|
||||||
|
ini.SetString(name, key, strconv.FormatInt(value, 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFloat64 设置单个配置项的浮点数值
|
||||||
|
// 入参: name 分区名称, key 配置项名称, value 配置项值
|
||||||
|
func (ini *GoINI) SetFloat64(name string, key string, value float64) {
|
||||||
|
ini.SetString(name, key, strconv.FormatFloat(value, 'f', -1, 64))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetComment 设置配置项注释,空注释会删除已有注释
|
||||||
|
// 入参: name 分区名称, key 配置项名称, comment 注释内容
|
||||||
|
// 返回: bool 是否设置成功
|
||||||
|
func (ini *GoINI) SetComment(name string, key string, comment string) bool {
|
||||||
|
if key == "" {
|
||||||
|
return ini.SetSectionComment(name, comment)
|
||||||
|
}
|
||||||
|
ini.ensureParsed()
|
||||||
|
name = ini.normalizeSection(name)
|
||||||
|
key = strings.TrimSpace(key)
|
||||||
|
index, ok := ini.findSectionKeyLineIndex(name, key)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ini.replaceCommentBefore(index, comment)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSectionComment 设置分区注释,空注释会删除已有注释
|
||||||
|
// 入参: name 分区名称, comment 注释内容
|
||||||
|
// 返回: bool 是否设置成功
|
||||||
|
func (ini *GoINI) SetSectionComment(name string, comment string) bool {
|
||||||
|
ini.ensureParsed()
|
||||||
|
name = ini.normalizeSection(name)
|
||||||
|
if name == ini.rootSection {
|
||||||
|
ini.replaceCommentBefore(ini.firstRootLineIndex(), comment)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ini.ensureSectionLine(name)
|
||||||
|
index, ok := ini.findSectionLineIndex(name)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ini.replaceCommentBefore(index, comment)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// GetNames 获取配置文件分区列表
|
// GetNames 获取配置文件分区列表
|
||||||
|
// 入参: match 分区名称正则表达式
|
||||||
|
// 返回: []string 分区名称列表
|
||||||
func (ini *GoINI) GetNames(match string) []string {
|
func (ini *GoINI) GetNames(match string) []string {
|
||||||
if match == "" {
|
return matchStrings(ini.sectionOrder, match)
|
||||||
return ini.nameList
|
|
||||||
}
|
|
||||||
var matchNameList []string
|
|
||||||
for _, name := range ini.nameList {
|
|
||||||
if b, e := regexp.MatchString(match, name); e == nil && b {
|
|
||||||
matchNameList = append(matchNameList, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matchNameList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNameKeys 获取分区下配置项列表
|
// GetNameKeys 获取分区下配置项列表
|
||||||
|
// 入参: name 分区名称, match 配置项名称正则表达式
|
||||||
|
// 返回: []string 配置项名称列表
|
||||||
func (ini *GoINI) GetNameKeys(name string, match string) []string {
|
func (ini *GoINI) GetNameKeys(name string, match string) []string {
|
||||||
var keyList []string
|
name = ini.normalizeSection(name)
|
||||||
if name == "" {
|
return matchStrings(ini.sectionKeys[name], match)
|
||||||
name = ini.commonField
|
|
||||||
}
|
}
|
||||||
if keyList, ok := ini.dataKeyList[name]; ok {
|
|
||||||
if match == "" {
|
// GetString 获取单个配置项的字符串值
|
||||||
return keyList
|
// 入参: name 分区名称, key 配置项名称, value 默认值
|
||||||
}
|
// 返回: string 配置项值
|
||||||
var matchKeyList []string
|
func (ini *GoINI) GetString(name string, key string, value string) string {
|
||||||
for _, key := range keyList {
|
name = ini.normalizeSection(name)
|
||||||
if b, e := regexp.MatchString(match, key); e == nil && b {
|
if v, ok := ini.sectionValues[name]; ok {
|
||||||
matchKeyList = append(matchKeyList, key)
|
if vv, ok := v[key]; ok {
|
||||||
|
return vv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return matchKeyList
|
return value
|
||||||
}
|
|
||||||
return keyList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBool 获取单个配置项的布尔值
|
// GetBool 获取单个配置项的布尔值
|
||||||
|
// 入参: name 分区名称, key 配置项名称, value 默认值
|
||||||
|
// 返回: bool 配置项值
|
||||||
func (ini *GoINI) GetBool(name string, key string, value bool) bool {
|
func (ini *GoINI) GetBool(name string, key string, value bool) bool {
|
||||||
boolStr := ini.GetString(name, key, "")
|
boolStr := ini.GetString(name, key, "")
|
||||||
switch strings.ToLower(boolStr) {
|
switch strings.ToLower(boolStr) {
|
||||||
@@ -194,7 +216,9 @@ func (ini *GoINI) GetBool(name string, key string, value bool) bool {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInt64 获取单个配置项的数字值
|
// GetInt64 获取单个配置项的整数值
|
||||||
|
// 入参: name 分区名称, key 配置项名称, value 默认值
|
||||||
|
// 返回: int64 配置项值
|
||||||
func (ini *GoINI) GetInt64(name string, key string, value int64) int64 {
|
func (ini *GoINI) GetInt64(name string, key string, value int64) int64 {
|
||||||
int64Str := ini.GetString(name, key, "")
|
int64Str := ini.GetString(name, key, "")
|
||||||
if i, e := strconv.ParseInt(int64Str, 10, 64); e == nil {
|
if i, e := strconv.ParseInt(int64Str, 10, 64); e == nil {
|
||||||
@@ -203,20 +227,9 @@ func (ini *GoINI) GetInt64(name string, key string, value int64) int64 {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetString 获取单个配置项的字符值
|
// GetFloat64 获取单个配置项的浮点数值
|
||||||
func (ini *GoINI) GetString(name string, key string, value string) string {
|
// 入参: name 分区名称, key 配置项名称, value 默认值
|
||||||
if 0 == len(name) {
|
// 返回: float64 配置项值
|
||||||
name = ini.commonField
|
|
||||||
}
|
|
||||||
if v, ok := ini.dataMap[name]; ok {
|
|
||||||
if vv, ok := v[key]; ok {
|
|
||||||
return vv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFloat64 获取单个配置项的小数值
|
|
||||||
func (ini *GoINI) GetFloat64(name string, key string, value float64) float64 {
|
func (ini *GoINI) GetFloat64(name string, key string, value float64) float64 {
|
||||||
float64Str := ini.GetString(name, key, "")
|
float64Str := ini.GetString(name, key, "")
|
||||||
if f, e := strconv.ParseFloat(float64Str, 64); e == nil {
|
if f, e := strconv.ParseFloat(float64Str, 64); e == nil {
|
||||||
@@ -225,60 +238,56 @@ func (ini *GoINI) GetFloat64(name string, key string, value float64) float64 {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetComment 获取配置项注释
|
||||||
|
// 入参: name 分区名称, key 配置项名称
|
||||||
|
// 返回: string 注释内容
|
||||||
|
func (ini *GoINI) GetComment(name string, key string) string {
|
||||||
|
if key == "" {
|
||||||
|
return ini.GetSectionComment(name)
|
||||||
|
}
|
||||||
|
ini.ensureParsed()
|
||||||
|
name = ini.normalizeSection(name)
|
||||||
|
index, ok := ini.findSectionKeyLineIndex(name, strings.TrimSpace(key))
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return ini.commentBefore(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSectionComment 获取分区注释
|
||||||
|
// 入参: name 分区名称
|
||||||
|
// 返回: string 注释内容
|
||||||
|
func (ini *GoINI) GetSectionComment(name string) string {
|
||||||
|
ini.ensureParsed()
|
||||||
|
name = ini.normalizeSection(name)
|
||||||
|
if name == ini.rootSection {
|
||||||
|
return ini.commentBefore(ini.firstRootLineIndex())
|
||||||
|
}
|
||||||
|
index, ok := ini.findSectionLineIndex(name)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return ini.commentBefore(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTag 设置结构体的tag键名称
|
||||||
|
// 入参: tag tag键名称
|
||||||
|
func (ini *GoINI) SetTag(tag string) {
|
||||||
|
ini.structTag = tag
|
||||||
|
}
|
||||||
|
|
||||||
// MapToStruct 将配置映射到一个结构体
|
// MapToStruct 将配置映射到一个结构体
|
||||||
func (ini *GoINI) MapToStruct(ptr interface{}) (err error) {
|
// 入参: ptr 结构体指针
|
||||||
t := reflect.TypeOf(ptr)
|
// 返回: error 错误信息
|
||||||
|
func (ini *GoINI) MapToStruct(ptr interface{}) error {
|
||||||
v := reflect.ValueOf(ptr)
|
v := reflect.ValueOf(ptr)
|
||||||
if t.Kind() != reflect.Ptr {
|
if !v.IsValid() || v.Kind() != reflect.Ptr || v.IsNil() {
|
||||||
err = errors.New("input struct ptr")
|
return errStructPointerRequired
|
||||||
return
|
|
||||||
}
|
}
|
||||||
t = t.Elem()
|
|
||||||
v = v.Elem()
|
v = v.Elem()
|
||||||
for i := 0; i < t.NumField(); i++ {
|
if v.Kind() != reflect.Struct {
|
||||||
if !v.CanInterface() {
|
return errStructPointerRequired
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
k := t.Field(i).Tag.Get(ini.tag)
|
ini.mapStruct(v)
|
||||||
if k == "" {
|
return nil
|
||||||
k = t.Field(i).Name
|
|
||||||
}
|
|
||||||
switch v.Field(i).Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
tt := v.Field(i).Type()
|
|
||||||
vv := v.Field(i)
|
|
||||||
for ii := 0; ii < tt.NumField(); ii++ {
|
|
||||||
if !v.CanInterface() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
kk := tt.Field(ii).Tag.Get(ini.tag)
|
|
||||||
if kk == "" {
|
|
||||||
kk = t.Field(ii).Name
|
|
||||||
}
|
|
||||||
switch vv.Field(ii).Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
vv.Field(ii).SetBool(ini.GetBool(k, kk, false))
|
|
||||||
case reflect.String:
|
|
||||||
vv.Field(ii).SetString(ini.GetString(k, kk, ""))
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
vv.Field(ii).SetFloat(ini.GetFloat64(k, kk, 0))
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
vv.Field(ii).SetInt(ini.GetInt64(k, kk, 0))
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
vv.Field(ii).SetUint(uint64(ini.GetInt64(k, kk, 0)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Bool:
|
|
||||||
v.Field(i).SetBool(ini.GetBool(ini.commonField, k, false))
|
|
||||||
case reflect.String:
|
|
||||||
v.Field(i).SetString(ini.GetString(ini.commonField, k, ""))
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
v.Field(i).SetFloat(ini.GetFloat64(ini.commonField, k, 0))
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
v.Field(i).SetInt(ini.GetInt64(ini.commonField, k, 0))
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
v.Field(i).SetUint(uint64(ini.GetInt64(ini.commonField, k, 0)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|||||||
+492
@@ -0,0 +1,492 @@
|
|||||||
|
package goini
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
type lineKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
lineBlank lineKind = iota
|
||||||
|
lineComment
|
||||||
|
lineSection
|
||||||
|
lineKeyValue
|
||||||
|
lineRaw
|
||||||
|
)
|
||||||
|
|
||||||
|
type iniLine struct {
|
||||||
|
kind lineKind
|
||||||
|
text string
|
||||||
|
sectionName string
|
||||||
|
keyName string
|
||||||
|
keyValue string
|
||||||
|
indent string
|
||||||
|
separator string
|
||||||
|
modified bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type keyValueParts struct {
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
indent string
|
||||||
|
separator string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) parse() {
|
||||||
|
ini.fileLines = nil
|
||||||
|
currentSection := ini.rootSection
|
||||||
|
if len(ini.data) == 0 {
|
||||||
|
ini.rebuildIndexes()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, rawLine := range strings.Split(string(ini.data), "\n") {
|
||||||
|
line, section := parseRawLine(rawLine, currentSection)
|
||||||
|
currentSection = section
|
||||||
|
ini.fileLines = append(ini.fileLines, line)
|
||||||
|
}
|
||||||
|
ini.rebuildIndexes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRawLine(rawLine string, sectionName string) (iniLine, string) {
|
||||||
|
rawLine = strings.TrimSuffix(rawLine, "\r")
|
||||||
|
line := iniLine{kind: lineRaw, text: rawLine, sectionName: sectionName}
|
||||||
|
trimmed := strings.TrimSpace(rawLine)
|
||||||
|
if trimmed == "" {
|
||||||
|
line.kind = lineBlank
|
||||||
|
return line, sectionName
|
||||||
|
}
|
||||||
|
if isComment(trimmed) {
|
||||||
|
line.kind = lineComment
|
||||||
|
return line, sectionName
|
||||||
|
}
|
||||||
|
if nextSection, ok := parseSection(trimmed); ok {
|
||||||
|
line.kind = lineSection
|
||||||
|
line.sectionName = nextSection
|
||||||
|
return line, nextSection
|
||||||
|
}
|
||||||
|
if parts, ok := parseKeyValue(rawLine); ok {
|
||||||
|
line.kind = lineKeyValue
|
||||||
|
line.keyName = parts.key
|
||||||
|
line.keyValue = trimSurroundingQuotes(parts.value)
|
||||||
|
line.indent = parts.indent
|
||||||
|
line.separator = parts.separator
|
||||||
|
return line, sectionName
|
||||||
|
}
|
||||||
|
return line, sectionName
|
||||||
|
}
|
||||||
|
|
||||||
|
func isComment(line string) bool {
|
||||||
|
return strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSection(line string) (string, bool) {
|
||||||
|
if !strings.HasPrefix(line, "[") || !strings.HasSuffix(line, "]") {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
name := strings.TrimSpace(line[1 : len(line)-1])
|
||||||
|
return name, name != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseKeyValue(line string) (keyValueParts, bool) {
|
||||||
|
indentLen := leadingSpaceLen(line)
|
||||||
|
indent := line[:indentLen]
|
||||||
|
body := line[indentLen:]
|
||||||
|
if split := strings.IndexByte(body, '='); split >= 0 {
|
||||||
|
return parseKeyValueParts(body, split, split+1, indent)
|
||||||
|
}
|
||||||
|
if split := strings.IndexFunc(body, unicode.IsSpace); split >= 0 {
|
||||||
|
return parseKeyValueParts(body, split, split, indent)
|
||||||
|
}
|
||||||
|
return keyValueParts{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseKeyValueParts(body string, keyEnd int, valueStart int, indent string) (keyValueParts, bool) {
|
||||||
|
keyPart := body[:keyEnd]
|
||||||
|
valuePart := body[valueStart:]
|
||||||
|
key := strings.TrimSpace(keyPart)
|
||||||
|
if key == "" {
|
||||||
|
return keyValueParts{}, false
|
||||||
|
}
|
||||||
|
separatorStart := len(strings.TrimRightFunc(keyPart, unicode.IsSpace))
|
||||||
|
separatorEnd := valueStart + leadingSpaceLen(valuePart)
|
||||||
|
return keyValueParts{
|
||||||
|
key: key,
|
||||||
|
value: strings.TrimSpace(valuePart),
|
||||||
|
indent: indent,
|
||||||
|
separator: body[separatorStart:separatorEnd],
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func leadingSpaceLen(text string) int {
|
||||||
|
return len(text) - len(strings.TrimLeftFunc(text, unicode.IsSpace))
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimSurroundingQuotes(value string) string {
|
||||||
|
if !hasSurroundingQuotes(value) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return value[1 : len(value)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasSurroundingQuotes(value string) bool {
|
||||||
|
if len(value) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
first, last := value[0], value[len(value)-1]
|
||||||
|
return first == last && (first == '"' || first == '\'' || first == '`')
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) rebuildIndexes() {
|
||||||
|
ini.sectionKeys = make(map[string][]string)
|
||||||
|
ini.sectionOrder = nil
|
||||||
|
ini.sectionValues = make(map[string]map[string]string)
|
||||||
|
ini.sectionLineIndexes = make(map[string]int)
|
||||||
|
ini.sectionKeyLineIndexes = make(map[string]map[string]int)
|
||||||
|
ini.addSection(ini.rootSection)
|
||||||
|
for i, line := range ini.fileLines {
|
||||||
|
switch line.kind {
|
||||||
|
case lineSection:
|
||||||
|
ini.addSectionLineIndex(line.sectionName, i)
|
||||||
|
case lineKeyValue:
|
||||||
|
ini.addSectionKey(line.sectionName, line.keyName, line.keyValue, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) addSection(sectionName string) {
|
||||||
|
if _, ok := ini.sectionValues[sectionName]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ini.sectionValues[sectionName] = make(map[string]string)
|
||||||
|
ini.sectionOrder = append(ini.sectionOrder, sectionName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) addSectionLineIndex(sectionName string, lineIndex int) {
|
||||||
|
if _, ok := ini.sectionLineIndexes[sectionName]; !ok {
|
||||||
|
ini.sectionLineIndexes[sectionName] = lineIndex
|
||||||
|
}
|
||||||
|
ini.addSection(sectionName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) addSectionKey(sectionName string, keyName string, keyValue string, lineIndex int) {
|
||||||
|
ini.addSection(sectionName)
|
||||||
|
if _, ok := ini.sectionValues[sectionName][keyName]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ini.sectionValues[sectionName][keyName] = keyValue
|
||||||
|
ini.sectionKeys[sectionName] = append(ini.sectionKeys[sectionName], keyName)
|
||||||
|
if ini.sectionKeyLineIndexes[sectionName] == nil {
|
||||||
|
ini.sectionKeyLineIndexes[sectionName] = make(map[string]int)
|
||||||
|
}
|
||||||
|
ini.sectionKeyLineIndexes[sectionName][keyName] = lineIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) ensureParsed() {
|
||||||
|
if ini.sectionValues == nil {
|
||||||
|
ini.parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) normalizeSection(sectionName string) string {
|
||||||
|
if sectionName == "" {
|
||||||
|
return ini.rootSection
|
||||||
|
}
|
||||||
|
return sectionName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) findSectionLineIndex(sectionName string) (int, bool) {
|
||||||
|
index, ok := ini.sectionLineIndexes[sectionName]
|
||||||
|
return index, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) findSectionKeyLineIndex(sectionName string, keyName string) (int, bool) {
|
||||||
|
if ini.sectionKeyLineIndexes[sectionName] == nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
index, ok := ini.sectionKeyLineIndexes[sectionName][keyName]
|
||||||
|
return index, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) ensureSectionLine(sectionName string) {
|
||||||
|
if sectionName == ini.rootSection {
|
||||||
|
ini.addSection(sectionName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok := ini.sectionValues[sectionName]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(ini.fileLines) > 0 && renderLine(ini.fileLines[len(ini.fileLines)-1]) != "" {
|
||||||
|
ini.fileLines = append(ini.fileLines, iniLine{kind: lineBlank})
|
||||||
|
}
|
||||||
|
ini.fileLines = append(ini.fileLines, iniLine{kind: lineSection, sectionName: sectionName, text: "[" + sectionName + "]"})
|
||||||
|
ini.rebuildIndexes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) findSectionInsertIndex(sectionName string) int {
|
||||||
|
if sectionName == ini.rootSection {
|
||||||
|
return ini.findRootInsertIndex()
|
||||||
|
}
|
||||||
|
current := ini.rootSection
|
||||||
|
insert := len(ini.fileLines)
|
||||||
|
for i, line := range ini.fileLines {
|
||||||
|
if line.kind == lineSection {
|
||||||
|
current = line.sectionName
|
||||||
|
if current == sectionName {
|
||||||
|
insert = i + 1
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if current == sectionName {
|
||||||
|
insert = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return insert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) findRootInsertIndex() int {
|
||||||
|
for i, line := range ini.fileLines {
|
||||||
|
if line.kind == lineSection {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(ini.fileLines)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) insertLine(index int, line iniLine) {
|
||||||
|
if index < 0 || index > len(ini.fileLines) {
|
||||||
|
index = len(ini.fileLines)
|
||||||
|
}
|
||||||
|
ini.fileLines = append(ini.fileLines, iniLine{})
|
||||||
|
copy(ini.fileLines[index+1:], ini.fileLines[index:])
|
||||||
|
ini.fileLines[index] = line
|
||||||
|
ini.rebuildIndexes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) replaceLines(start int, end int, replacement []iniLine) {
|
||||||
|
next := make([]iniLine, 0, len(ini.fileLines)-(end-start)+len(replacement))
|
||||||
|
next = append(next, ini.fileLines[:start]...)
|
||||||
|
next = append(next, replacement...)
|
||||||
|
next = append(next, ini.fileLines[end:]...)
|
||||||
|
ini.fileLines = next
|
||||||
|
ini.rebuildIndexes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) replaceCommentBefore(index int, comment string) {
|
||||||
|
start, end := ini.commentRangeBefore(index)
|
||||||
|
ini.replaceLines(start, end, commentLines(comment))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) commentRangeBefore(index int) (int, int) {
|
||||||
|
start := index
|
||||||
|
for start > 0 && ini.fileLines[start-1].kind == lineComment {
|
||||||
|
start--
|
||||||
|
}
|
||||||
|
return start, index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) commentBefore(index int) string {
|
||||||
|
start, end := ini.commentRangeBefore(index)
|
||||||
|
comments := make([]string, 0, end-start)
|
||||||
|
for _, line := range ini.fileLines[start:end] {
|
||||||
|
comments = append(comments, stripComment(line.text))
|
||||||
|
}
|
||||||
|
return strings.Join(comments, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) firstRootLineIndex() int {
|
||||||
|
for i, line := range ini.fileLines {
|
||||||
|
if line.kind == lineKeyValue && line.sectionName == ini.rootSection {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
if line.kind == lineSection {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func commentLines(comment string) []iniLine {
|
||||||
|
if comment == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
parts := strings.Split(strings.ReplaceAll(comment, "\r\n", "\n"), "\n")
|
||||||
|
lines := make([]iniLine, 0, len(parts))
|
||||||
|
for _, part := range parts {
|
||||||
|
lines = append(lines, iniLine{kind: lineComment, text: formatComment(part)})
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatComment(comment string) string {
|
||||||
|
trimmed := strings.TrimLeftFunc(comment, unicode.IsSpace)
|
||||||
|
if strings.HasPrefix(trimmed, "#") || strings.HasPrefix(trimmed, ";") {
|
||||||
|
return comment
|
||||||
|
}
|
||||||
|
if comment == "" {
|
||||||
|
return "#"
|
||||||
|
}
|
||||||
|
return "# " + comment
|
||||||
|
}
|
||||||
|
|
||||||
|
func stripComment(comment string) string {
|
||||||
|
trimmed := strings.TrimSpace(comment)
|
||||||
|
if !isComment(trimmed) {
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
trimmed = strings.TrimPrefix(strings.TrimPrefix(trimmed, "#"), ";")
|
||||||
|
return strings.TrimPrefix(trimmed, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderLine(line iniLine) string {
|
||||||
|
switch line.kind {
|
||||||
|
case lineSection:
|
||||||
|
if line.text != "" && !line.modified {
|
||||||
|
return line.text
|
||||||
|
}
|
||||||
|
return "[" + line.sectionName + "]"
|
||||||
|
case lineKeyValue:
|
||||||
|
if line.text != "" && !line.modified {
|
||||||
|
return line.text
|
||||||
|
}
|
||||||
|
return renderKeyValue(line)
|
||||||
|
default:
|
||||||
|
return line.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderKeyValue(line iniLine) string {
|
||||||
|
separator := line.separator
|
||||||
|
if separator == "" {
|
||||||
|
separator = defaultSeparator
|
||||||
|
}
|
||||||
|
return line.indent + line.keyName + separator + formatValue(line.keyValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatValue(value string) string {
|
||||||
|
if strings.TrimSpace(value) == value && !hasSurroundingQuotes(value) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
quote := `"`
|
||||||
|
if strings.HasPrefix(value, `"`) && strings.HasSuffix(value, `"`) {
|
||||||
|
quote = "`"
|
||||||
|
}
|
||||||
|
return quote + value + quote
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchStrings(items []string, match string) []string {
|
||||||
|
if match == "" {
|
||||||
|
return cloneStrings(items)
|
||||||
|
}
|
||||||
|
re, err := regexp.Compile(match)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
matches := make([]string, 0, len(items))
|
||||||
|
for _, item := range items {
|
||||||
|
if re.MatchString(item) {
|
||||||
|
matches = append(matches, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matches
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneStrings(items []string) []string {
|
||||||
|
if items == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cloned := make([]string, len(items))
|
||||||
|
copy(cloned, items)
|
||||||
|
return cloned
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) mapStruct(v reflect.Value) {
|
||||||
|
t := v.Type()
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
field := v.Field(i)
|
||||||
|
if !field.CanSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, ok := ini.fieldName(t.Field(i))
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if field.Kind() == reflect.Struct {
|
||||||
|
ini.mapSection(field, name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ini.setField(field, ini.rootSection, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) mapSection(v reflect.Value, section string) {
|
||||||
|
t := v.Type()
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
field := v.Field(i)
|
||||||
|
if !field.CanSet() || field.Kind() == reflect.Struct {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, ok := ini.fieldName(t.Field(i))
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ini.setField(field, section, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) fieldName(field reflect.StructField) (string, bool) {
|
||||||
|
if ini.structTag != "" {
|
||||||
|
if tag, ok := field.Tag.Lookup(ini.structTag); ok {
|
||||||
|
name := strings.SplitN(tag, ",", 2)[0]
|
||||||
|
if name == "-" {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if name != "" {
|
||||||
|
return name, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return field.Name, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) setField(field reflect.Value, section string, key string) {
|
||||||
|
switch field.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
field.SetBool(ini.GetBool(section, key, false))
|
||||||
|
case reflect.String:
|
||||||
|
field.SetString(ini.GetString(section, key, ""))
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
field.SetFloat(ini.parseFloat(section, key, field.Type().Bits()))
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
field.SetInt(ini.parseInt(section, key, field.Type().Bits()))
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
field.SetUint(ini.parseUint(section, key, field.Type().Bits()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) parseFloat(section string, key string, bitSize int) float64 {
|
||||||
|
value, err := strconv.ParseFloat(ini.GetString(section, key, ""), bitSize)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) parseInt(section string, key string, bitSize int) int64 {
|
||||||
|
value, err := strconv.ParseInt(ini.GetString(section, key, ""), 10, bitSize)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ini *GoINI) parseUint(section string, key string, bitSize int) uint64 {
|
||||||
|
value, err := strconv.ParseUint(ini.GetString(section, key, ""), 10, bitSize)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user