github.com/wangyougui/gf/v2@v2.6.5/encoding/gini/gini.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  // Package gini provides accessing and converting for INI content.
     8  package gini
     9  
    10  import (
    11  	"bufio"
    12  	"bytes"
    13  	"fmt"
    14  	"io"
    15  	"strings"
    16  
    17  	"github.com/wangyougui/gf/v2/errors/gcode"
    18  	"github.com/wangyougui/gf/v2/errors/gerror"
    19  	"github.com/wangyougui/gf/v2/internal/json"
    20  )
    21  
    22  // Decode converts INI format to map.
    23  func Decode(data []byte) (res map[string]interface{}, err error) {
    24  	res = make(map[string]interface{})
    25  	var (
    26  		fieldMap    = make(map[string]interface{})
    27  		bytesReader = bytes.NewReader(data)
    28  		bufioReader = bufio.NewReader(bytesReader)
    29  		section     string
    30  		lastSection string
    31  		haveSection bool
    32  		line        string
    33  	)
    34  
    35  	for {
    36  		line, err = bufioReader.ReadString('\n')
    37  		if err != nil {
    38  			if err == io.EOF {
    39  				break
    40  			}
    41  			err = gerror.Wrapf(err, `bufioReader.ReadString failed`)
    42  			return nil, err
    43  		}
    44  		if line = strings.TrimSpace(line); len(line) == 0 {
    45  			continue
    46  		}
    47  
    48  		if line[0] == ';' || line[0] == '#' {
    49  			continue
    50  		}
    51  		var (
    52  			sectionBeginPos = strings.Index(line, "[")
    53  			sectionEndPos   = strings.Index(line, "]")
    54  		)
    55  		if sectionBeginPos >= 0 && sectionEndPos >= 2 {
    56  			section = line[sectionBeginPos+1 : sectionEndPos]
    57  			if lastSection == "" {
    58  				lastSection = section
    59  			} else if lastSection != section {
    60  				lastSection = section
    61  				fieldMap = make(map[string]interface{})
    62  			}
    63  			haveSection = true
    64  		} else if !haveSection {
    65  			continue
    66  		}
    67  
    68  		if strings.Contains(line, "=") && haveSection {
    69  			values := strings.Split(line, "=")
    70  			fieldMap[strings.TrimSpace(values[0])] = strings.TrimSpace(strings.Join(values[1:], "="))
    71  			res[section] = fieldMap
    72  		}
    73  	}
    74  
    75  	if !haveSection {
    76  		return nil, gerror.NewCode(gcode.CodeInvalidParameter, "failed to parse INI file, section not found")
    77  	}
    78  	return res, nil
    79  }
    80  
    81  // Encode converts map to INI format.
    82  func Encode(data map[string]interface{}) (res []byte, err error) {
    83  	var (
    84  		n  int
    85  		w  = new(bytes.Buffer)
    86  		m  map[string]interface{}
    87  		ok bool
    88  	)
    89  	for section, item := range data {
    90  		// Section key-value pairs.
    91  		if m, ok = item.(map[string]interface{}); ok {
    92  			n, err = w.WriteString(fmt.Sprintf("[%s]\n", section))
    93  			if err != nil || n == 0 {
    94  				return nil, gerror.Wrapf(err, "w.WriteString failed")
    95  			}
    96  			for k, v := range m {
    97  				if n, err = w.WriteString(fmt.Sprintf("%s=%v\n", k, v)); err != nil || n == 0 {
    98  					return nil, gerror.Wrapf(err, "w.WriteString failed")
    99  				}
   100  			}
   101  			continue
   102  		}
   103  		// Simple key-value pairs.
   104  		for k, v := range data {
   105  			if n, err = w.WriteString(fmt.Sprintf("%s=%v\n", k, v)); err != nil || n == 0 {
   106  				return nil, gerror.Wrapf(err, "w.WriteString failed")
   107  			}
   108  		}
   109  		break
   110  	}
   111  	res = make([]byte, w.Len())
   112  	if n, err = w.Read(res); err != nil || n == 0 {
   113  		return nil, gerror.Wrapf(err, "w.Read failed")
   114  	}
   115  	return res, nil
   116  }
   117  
   118  // ToJson convert INI format to JSON.
   119  func ToJson(data []byte) (res []byte, err error) {
   120  	iniMap, err := Decode(data)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	return json.Marshal(iniMap)
   125  }