0
Files
joyproxy/vendor/github.com/wlevene/ini/ini.go
eleksir 79d9a2f604 Change layout to more go-style-ish, replace some modules
* Deejay files, split them to achieve go-style project layout
* Replace logrus with log/slog module to reduce dependencies
* Replace gopkg.in/ini.v1 with github.com/wlevene/ini to make sure all modules BSD licensed
* Update linter config && fix tons of linter warnings
* Update .gitignore
* Update README.md
2025-11-09 22:32:50 +03:00

598 lines
9.9 KiB
Go

package ini
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
"github.com/wlevene/ini/ast"
"github.com/wlevene/ini/lexer"
"github.com/wlevene/ini/parser"
"github.com/wlevene/ini/token"
"github.com/fsnotify/fsnotify"
)
// TODO: new section
type (
Ini struct {
currectSection *ast.SetcionNode
src []byte // source
l *lexer.Lexer
p *parser.Parser
doc *ast.Doc
err error
watcher *fsnotify.Watcher
exitWatchChan chan bool
}
)
func New() *Ini {
in := &Ini{
doc: &ast.Doc{},
}
return in
}
func (in *Ini) Err() error {
return in.err
}
func (in *Ini) LoadFile(file string) *Ini {
// read file content
bts, err := ioutil.ReadFile(file)
if err != nil {
in.err = err
return in
}
in.Load(bts)
return in
}
func (in *Ini) WatchFile(file string) *Ini {
in.LoadFile(file)
in.watch(file)
return in
}
func (in *Ini) watch(file string) {
if file == "" {
return
}
in.watcher, in.err = fsnotify.NewWatcher()
in.exitWatchChan = make(chan bool)
go func() {
for {
select {
case event, ok := <-in.watcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write == fsnotify.Write {
in.LoadFile(file)
}
case _, ok := <-in.watcher.Errors:
if !ok {
return
}
case <-in.exitWatchChan:
return
}
}
}()
in.err = in.watcher.Add(file)
}
func (in *Ini) StopWatch() *Ini {
in.watcher.Close()
in.exitWatchChan <- true
return in
}
func (in *Ini) Load(doc []byte) *Ini {
if len(doc) <= 0 {
return in
}
in.src = doc
in.l = lexer.New(string(in.src))
in.p = parser.New(in.l)
in.doc, in.err = in.p.ParseDocument()
return in
}
func (in *Ini) Dump() {
if in.doc == nil {
return
}
in.doc.DumpV2()
}
func (this *Ini) Marshal2Map() map[string]interface{} {
if this.doc == nil {
return nil
}
if this.err != nil {
return nil
}
kvMaps := make(map[string]interface{})
for c := this.doc.FirstChild(); c != nil; c = c.NextSibling() {
if kv_node, ok := c.(*ast.KVNode); ok {
kvMaps[kv_node.Key.Literal] = kv_node.Value.Literal
}
if sect_node, ok := c.(*ast.SetcionNode); ok {
secMap := make(map[string]interface{})
for kv := sect_node.FirstChild(); kv != nil; kv = kv.NextSibling() {
if kvnode, ok := kv.(*ast.KVNode); ok {
secMap[kvnode.Key.Literal] = kvnode.Value.Literal
}
}
kvMaps[sect_node.Name.Literal] = secMap
continue
}
}
return kvMaps
}
func (this *Ini) Marshal2Json() []byte {
kvMaps := this.Marshal2Map()
if kvMaps == nil {
return nil
}
result, err := json.Marshal(kvMaps)
this.err = err
return result
}
func (this *Ini) Section(section string) *Ini {
if this.doc == nil {
return this
}
if this.err != nil {
return this
}
this.sectionForAstDoc(section)
return this
}
func (this *Ini) Get(key string) string {
return this.GetDef(key, "")
}
func (this *Ini) GetDef(key string, def string) string {
if this.doc == nil ||
this.err != nil {
return def
}
if key == "" {
return def
}
tok := this.getToken(key)
if tok.Type != token.TokenType_VALUE {
return def
}
return tok.Literal
}
func (this *Ini) GetInt(key string) int {
return this.GetIntDef(key, 0)
}
func (this *Ini) GetIntDef(key string, def int) int {
val := this.Get(key)
if val == "" {
return def
}
ival, err := strconv.Atoi(val)
if err != nil {
return def
}
return ival
}
func (this *Ini) GetInt64(key string) int64 {
return this.GetInt64Def(key, 0)
}
func (this *Ini) GetInt64Def(key string, def int64) int64 {
val := this.Get(key)
if val == "" {
return def
}
ival, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return def
}
return ival
}
func (this *Ini) GetFloat64(key string) float64 {
return this.GetFloat64Def(key, 0)
}
func (this *Ini) GetFloat64Def(key string, def float64) float64 {
val := this.Get(key)
if val == "" {
return def
}
fval, err := strconv.ParseFloat(val, 64)
if err != nil {
return def
}
return fval
}
func (this *Ini) Set(key string, val interface{}) *Ini {
if this.doc == nil ||
this.err != nil {
return this
}
if key == "" || val == nil {
return this
}
var val_str string
switch val.(type) {
case int:
val_str = fmt.Sprintf("%d", val.(int))
case int32:
val_str = fmt.Sprintf("%d", val.(int32))
case int64:
val_str = fmt.Sprintf("%d", val.(int64))
case float32:
val_str = strconv.FormatFloat(float64(val.(float32)), 'f', -1, 32)
case float64:
val_str = strconv.FormatFloat(float64(val.(float64)), 'f', -1, 64)
case string:
val_str = val.(string)
default:
return this
}
val_str = strings.Replace(val_str, "\n", "", -1)
val_str = strings.Replace(val_str, "\t", "", -1)
val_str = strings.Trim(val_str, " ")
this.setKVNode(key, val_str)
return this
}
func (this *Ini) Del(key string) *Ini {
if this.doc == nil ||
this.err != nil {
return this
}
if key == "" {
return this
}
if this.currectSection == nil {
for c := this.doc.FirstChild(); c != nil; c = c.NextSibling() {
if kv_node, ok := c.(*ast.KVNode); ok {
if kv_node.Key.Literal == key {
this.doc.RemoveChild(this.doc, kv_node)
break
}
continue
}
// if sect_node, ok := c.(*ast.SetcionNode); ok {
// continue
// }
}
} else {
for c := this.currectSection.FirstChild(); c != nil; c = c.NextSibling() {
kvnodev2 := c.(*ast.KVNode)
if kvnodev2.Key.Literal == key {
this.currectSection.RemoveChild(this.currectSection, c)
break
}
}
}
return this
}
func (this *Ini) DelSection(section string) *Ini {
if this.doc == nil ||
this.err != nil {
return this
}
if section == "" {
return this
}
if this.currectSection == nil {
for c := this.doc.FirstChild(); c != nil; c = c.NextSibling() {
if sect_node, ok := c.(*ast.SetcionNode); ok {
if sect_node.Name.Literal == section {
this.doc.RemoveChild(this.doc, sect_node)
break
}
}
}
}
return this
}
// TODO: implement Save
func (this *Ini) Save(filename string) *Ini {
if filename == "" {
return this
}
if this.doc == nil {
return this
}
var result string
is_last_type_comment := false
for c := this.doc.FirstChild(); c != nil; c = c.NextSibling() {
if kv_node, ok := c.(*ast.KVNode); ok {
is_last_type_comment = false
result = fmt.Sprintf("%s%s = %v\n", result, kv_node.Key.Literal, kv_node.Value.Literal)
continue
}
if sect_node, ok := c.(*ast.SetcionNode); ok {
if !is_last_type_comment {
result = fmt.Sprintf("%s\n", result)
}
is_last_type_comment = false
result = fmt.Sprintf("%s[%s]\n", result, sect_node.Name.Literal)
for c := sect_node.FirstChild(); c != nil; c = c.NextSibling() {
if kv_node, ok := c.(*ast.KVNode); ok {
result = fmt.Sprintf("%s%s = %v\n", result, kv_node.Key.Literal, kv_node.Value.Literal)
continue
}
}
continue
}
if comm_node, ok := c.(*ast.CommentNode); ok {
if !is_last_type_comment {
result = fmt.Sprintf("%s\n", result)
}
is_last_type_comment = true
result = fmt.Sprintf("%s%s\n", result, comm_node.Comment.Literal)
}
}
this.err = os.Remove(filename)
if this.err != nil {
return this
}
var data = []byte(result)
this.err = ioutil.WriteFile(filename, data, 0666)
return this
}
// ----------------------------------------------------------------
func (this *Ini) sectionForAstDoc(section string) {
if this.doc == nil ||
this.err != nil {
return
}
this.currectSection = nil
if section == "" {
return
}
for c := this.doc.FirstChild(); c != nil; c = c.NextSibling() {
if sect_node, ok := c.(*ast.SetcionNode); ok {
if sect_node.Name.Literal == section {
this.currectSection = sect_node
return
}
}
}
this.currectSection = &ast.SetcionNode{
Name: token.Token{
Type: token.TokenType_SECTION,
Literal: section,
},
}
this.doc.AppendChild(this.doc, this.currectSection)
}
func (this *Ini) getToken(key string) token.Token {
var tok token.Token
if key == "" {
return tok
}
if this.currectSection == nil {
for c := this.doc.FirstChild(); c != nil; c = c.NextSibling() {
if kv_node, ok := c.(*ast.KVNode); ok {
if kv_node.Key.Literal == key {
tok = kv_node.Value
return tok
}
continue
}
}
} else {
for c := this.currectSection.FirstChild(); c != nil; c = c.NextSibling() {
kvnodev2 := c.(*ast.KVNode)
if kvnodev2.Key.Literal == key {
tok = kvnodev2.Value
return tok
}
}
}
return tok
}
func (this *Ini) setKVNode(key string, val string) *Ini {
if key == "" || val == "" {
return this
}
line := 1
found := false
if this.currectSection == nil {
var last_kv_node *ast.KVNode
for c := this.doc.FirstChild(); c != nil; c = c.NextSibling() {
if kv_node, ok := c.(*ast.KVNode); ok {
last_kv_node = kv_node
if kv_node.Key.Literal == key {
kv_node.Value.Literal = val
return this
}
continue
}
// if sect_node, ok := c.(*ast.SetcionNode); ok {
// continue
// }
}
kvnode := &ast.KVNode{
Key: token.Token{
Type: token.TokenType_KEY,
Literal: key,
Line: line,
},
Value: token.Token{
Type: token.TokenType_VALUE,
Literal: val,
Line: line,
},
}
if last_kv_node == nil {
this.doc.AppendChild(this.doc, kvnode)
} else {
this.doc.InsertAfter(this.doc, last_kv_node, kvnode)
}
this.re_adjust_node_line()
} else {
for c := this.currectSection.FirstChild(); c != nil; c = c.NextSibling() {
kvnodev2 := c.(*ast.KVNode)
line = kvnodev2.Key.Line + 1
if kvnodev2.Key.Literal == key {
kvnodev2.Value.Literal = val
return this
}
}
if found == false {
kvnode := &ast.KVNode{
Key: token.Token{
Type: token.TokenType_KEY,
Literal: key,
Line: line,
},
Value: token.Token{
Type: token.TokenType_VALUE,
Literal: val,
Line: line,
},
}
this.currectSection.AppendChild(this.currectSection, kvnode)
this.re_adjust_node_line()
}
}
return this
}
// TODO: support
func (this *Ini) re_adjust_node_line() {
}