github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/goconfig/conf.go (about)

     1  // Copyright 2013 Unknwon
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  // Package goconfig is a fully functional and comments-support configuration file(.ini) parser.
    16  package goconfig
    17  
    18  import (
    19  	"fmt"
    20  	"regexp"
    21  	"runtime"
    22  	"strconv"
    23  	"strings"
    24  	"sync"
    25  )
    26  
    27  const (
    28  	// Default section name.
    29  	DEFAULT_SECTION = "DEFAULT"
    30  	// Maximum allowed depth when recursively substituing variable names.
    31  	_DEPTH_VALUES = 200
    32  )
    33  
    34  // Parse error types.
    35  const (
    36  	ErrSectionNotFound = iota + 1
    37  	ErrKeyNotFound
    38  	ErrBlankSectionName
    39  	ErrCouldNotParse
    40  )
    41  
    42  var LineBreak = "\n"
    43  
    44  // Variable regexp pattern: %(variable)s
    45  var varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`)
    46  
    47  func init() {
    48  	if runtime.GOOS == "windows" {
    49  		LineBreak = "\r\n"
    50  	}
    51  }
    52  
    53  // A ConfigFile represents a INI formar configuration file.
    54  type ConfigFile struct {
    55  	lock      sync.RWMutex                 // Go map is not safe.
    56  	fileNames []string                     // Support mutil-files.
    57  	data      map[string]map[string]string // Section -> key : value
    58  
    59  	// Lists can keep sections and keys in order.
    60  	sectionList []string            // Section name list.
    61  	keyList     map[string][]string // Section -> Key name list
    62  
    63  	sectionComments map[string]string            // Sections comments.
    64  	keyComments     map[string]map[string]string // Keys comments.
    65  	BlockMode       bool                         // Indicates whether use lock or not.
    66  }
    67  
    68  // newConfigFile creates an empty configuration representation.
    69  func newConfigFile(fileNames []string) *ConfigFile {
    70  	c := new(ConfigFile)
    71  	c.fileNames = fileNames
    72  	c.data = make(map[string]map[string]string)
    73  	c.keyList = make(map[string][]string)
    74  	c.sectionComments = make(map[string]string)
    75  	c.keyComments = make(map[string]map[string]string)
    76  	c.BlockMode = true
    77  	return c
    78  }
    79  
    80  // SetValue adds a new section-key-value to the configuration.
    81  // It returns true if the key and value were inserted,
    82  // or returns false if the value was overwritten.
    83  // If the section does not exist in advance, it will be created.
    84  func (c *ConfigFile) SetValue(section, key, value string) bool {
    85  	if c.BlockMode {
    86  		c.lock.Lock()
    87  		defer c.lock.Unlock()
    88  	}
    89  
    90  	// Blank section name represents DEFAULT section.
    91  	if len(section) == 0 {
    92  		section = DEFAULT_SECTION
    93  	}
    94  
    95  	// Check if section exists.
    96  	if _, ok := c.data[section]; !ok {
    97  		// Execute add operation.
    98  		c.data[section] = make(map[string]string)
    99  		// Append section to list.
   100  		c.sectionList = append(c.sectionList, section)
   101  	}
   102  
   103  	// Check if key exists.
   104  	_, ok := c.data[section][key]
   105  	c.data[section][key] = value
   106  	if !ok {
   107  		// If not exists, append to key list.
   108  		c.keyList[section] = append(c.keyList[section], key)
   109  	}
   110  	return !ok
   111  }
   112  
   113  // DeleteKey deletes the key in given section.
   114  // It returns true if the key was deleted,
   115  // or returns false if the section or key didn't exist.
   116  func (c *ConfigFile) DeleteKey(section, key string) bool {
   117  	// Blank section name represents DEFAULT section.
   118  	if len(section) == 0 {
   119  		section = DEFAULT_SECTION
   120  	}
   121  
   122  	// Check if section exists.
   123  	if _, ok := c.data[section]; !ok {
   124  		return false
   125  	}
   126  
   127  	// Check if key exists.
   128  	if _, ok := c.data[section][key]; ok {
   129  		delete(c.data[section], key)
   130  		// Remove comments of key.
   131  		c.SetKeyComments(section, key, "")
   132  		// Get index of key.
   133  		i := 0
   134  		for _, keyName := range c.keyList[section] {
   135  			if keyName == key {
   136  				break
   137  			}
   138  			i++
   139  		}
   140  		// Remove from key list.
   141  		c.keyList[section] = append(c.keyList[section][:i], c.keyList[section][i+1:]...)
   142  		return true
   143  	}
   144  	return false
   145  }
   146  
   147  // GetValue returns the value of key available in the given section.
   148  // If the value needs to be unfolded
   149  // (see e.g. %(google)s example in the GoConfig_test.go),
   150  // then String does this unfolding automatically, up to
   151  // _DEPTH_VALUES number of iterations.
   152  // It returns an error and empty string value if the section does not exist,
   153  // or key does not exist in DEFAULT and current sections.
   154  func (c *ConfigFile) GetValue(section, key string) (string, error) {
   155  	if c.BlockMode {
   156  		c.lock.RLock()
   157  		defer c.lock.RUnlock()
   158  	}
   159  
   160  	// Blank section name represents DEFAULT section.
   161  	if len(section) == 0 {
   162  		section = DEFAULT_SECTION
   163  	}
   164  
   165  	// Check if section exists
   166  	if _, ok := c.data[section]; !ok {
   167  		// Section does not exist.
   168  		return "", getError{ErrSectionNotFound, section}
   169  	}
   170  
   171  	// Section exists.
   172  	// Check if key exists or empty value.
   173  	value, ok := c.data[section][key]
   174  	if !ok {
   175  		// Check if it is a sub-section.
   176  		if i := strings.LastIndex(section, "."); i > -1 {
   177  			return c.GetValue(section[:i], key)
   178  		}
   179  
   180  		// Return empty value.
   181  		return "", getError{ErrKeyNotFound, key}
   182  	}
   183  
   184  	// Key exists.
   185  	var i int
   186  	for i = 0; i < _DEPTH_VALUES; i++ {
   187  		vr := varPattern.FindString(value)
   188  		if len(vr) == 0 {
   189  			break
   190  		}
   191  
   192  		// Take off leading '%(' and trailing ')s'.
   193  		noption := strings.TrimLeft(vr, "%(")
   194  		noption = strings.TrimRight(noption, ")s")
   195  
   196  		// Search variable in default section.
   197  		nvalue, err := c.GetValue(DEFAULT_SECTION, noption)
   198  		if err != nil && section != DEFAULT_SECTION {
   199  			// Search in the same section.
   200  			if _, ok := c.data[section][noption]; ok {
   201  				nvalue = c.data[section][noption]
   202  			}
   203  		}
   204  
   205  		// Substitute by new value and take off leading '%(' and trailing ')s'.
   206  		value = strings.Replace(value, vr, nvalue, -1)
   207  	}
   208  	return value, nil
   209  }
   210  
   211  // Bool returns bool type value.
   212  func (c *ConfigFile) Bool(section, key string) (bool, error) {
   213  	value, err := c.GetValue(section, key)
   214  	if err != nil {
   215  		return false, err
   216  	}
   217  	return strconv.ParseBool(value)
   218  }
   219  
   220  // Float64 returns float64 type value.
   221  func (c *ConfigFile) Float64(section, key string) (float64, error) {
   222  	value, err := c.GetValue(section, key)
   223  	if err != nil {
   224  		return 0.0, err
   225  	}
   226  	return strconv.ParseFloat(value, 64)
   227  }
   228  
   229  // Int returns int type value.
   230  func (c *ConfigFile) Int(section, key string) (int, error) {
   231  	value, err := c.GetValue(section, key)
   232  	if err != nil {
   233  		return 0, err
   234  	}
   235  	return strconv.Atoi(value)
   236  }
   237  
   238  // Int64 returns int64 type value.
   239  func (c *ConfigFile) Int64(section, key string) (int64, error) {
   240  	value, err := c.GetValue(section, key)
   241  	if err != nil {
   242  		return 0, err
   243  	}
   244  	return strconv.ParseInt(value, 10, 64)
   245  }
   246  
   247  // MustValue always returns value without error.
   248  // It returns empty string if error occurs, or the default value if given.
   249  func (c *ConfigFile) MustValue(section, key string, defaultVal ...string) string {
   250  	val, err := c.GetValue(section, key)
   251  	if len(defaultVal) > 0 && (err != nil || len(val) == 0) {
   252  		return defaultVal[0]
   253  	}
   254  	return val
   255  }
   256  
   257  // MustValue always returns value without error,
   258  // It returns empty string if error occurs, or the default value if given,
   259  // and a bool value indicates whether default value is returned.
   260  func (c *ConfigFile) MustValueSet(section, key string, defaultVal ...string) (string, bool) {
   261  	val, err := c.GetValue(section, key)
   262  	if len(defaultVal) > 0 && (err != nil || len(val) == 0) {
   263  		c.SetValue(section, key, defaultVal[0])
   264  		return defaultVal[0], true
   265  	}
   266  	return val, false
   267  }
   268  
   269  // MustValueRange always returns value without error,
   270  // it returns default value if error occurs or doesn't fit into range.
   271  func (c *ConfigFile) MustValueRange(section, key, defaultVal string, candidates []string) string {
   272  	val, err := c.GetValue(section, key)
   273  	if err != nil || len(val) == 0 {
   274  		return defaultVal
   275  	}
   276  
   277  	for _, cand := range candidates {
   278  		if val == cand {
   279  			return val
   280  		}
   281  	}
   282  	return defaultVal
   283  }
   284  
   285  // MustValueArray always returns value array without error,
   286  // it returns empty array if error occurs, split by delimiter otherwise.
   287  func (c *ConfigFile) MustValueArray(section, key, delim string) []string {
   288  	val, err := c.GetValue(section, key)
   289  	if err != nil || len(val) == 0 {
   290  		return []string{}
   291  	}
   292  
   293  	vals := strings.Split(val, delim)
   294  	for i := range vals {
   295  		vals[i] = strings.TrimSpace(vals[i])
   296  	}
   297  	return vals
   298  }
   299  
   300  // MustBool always returns value without error,
   301  // it returns false if error occurs.
   302  func (c *ConfigFile) MustBool(section, key string, defaultVal ...bool) bool {
   303  	val, err := c.Bool(section, key)
   304  	if len(defaultVal) > 0 && err != nil {
   305  		return defaultVal[0]
   306  	}
   307  	return val
   308  }
   309  
   310  // MustFloat64 always returns value without error,
   311  // it returns 0.0 if error occurs.
   312  func (c *ConfigFile) MustFloat64(section, key string, defaultVal ...float64) float64 {
   313  	value, err := c.Float64(section, key)
   314  	if len(defaultVal) > 0 && err != nil {
   315  		return defaultVal[0]
   316  	}
   317  	return value
   318  }
   319  
   320  // MustInt always returns value without error,
   321  // it returns 0 if error occurs.
   322  func (c *ConfigFile) MustInt(section, key string, defaultVal ...int) int {
   323  	value, err := c.Int(section, key)
   324  	if len(defaultVal) > 0 && err != nil {
   325  		return defaultVal[0]
   326  	}
   327  	return value
   328  }
   329  
   330  // MustInt64 always returns value without error,
   331  // it returns 0 if error occurs.
   332  func (c *ConfigFile) MustInt64(section, key string, defaultVal ...int64) int64 {
   333  	value, err := c.Int64(section, key)
   334  	if len(defaultVal) > 0 && err != nil {
   335  		return defaultVal[0]
   336  	}
   337  	return value
   338  }
   339  
   340  // GetSectionList returns the list of all sections
   341  // in the same order in the file.
   342  func (c *ConfigFile) GetSectionList() []string {
   343  	list := make([]string, len(c.sectionList))
   344  	copy(list, c.sectionList)
   345  	return list
   346  }
   347  
   348  // GetKeyList returns the list of all keys in give section
   349  // in the same order in the file.
   350  // It returns nil if given section does not exist.
   351  func (c *ConfigFile) GetKeyList(section string) []string {
   352  	// Blank section name represents DEFAULT section.
   353  	if len(section) == 0 {
   354  		section = DEFAULT_SECTION
   355  	}
   356  
   357  	// Check if section exists.
   358  	if _, ok := c.data[section]; !ok {
   359  		return nil
   360  	}
   361  
   362  	// Non-default section has a blank key as section keeper.
   363  	offset := 1
   364  	if section == DEFAULT_SECTION {
   365  		offset = 0
   366  	}
   367  
   368  	list := make([]string, len(c.keyList[section])-offset)
   369  	copy(list, c.keyList[section][offset:])
   370  	return list
   371  }
   372  
   373  // DeleteSection deletes the entire section by given name.
   374  // It returns true if the section was deleted, and false if the section didn't exist.
   375  func (c *ConfigFile) DeleteSection(section string) bool {
   376  	// Blank section name represents DEFAULT section.
   377  	if len(section) == 0 {
   378  		section = DEFAULT_SECTION
   379  	}
   380  
   381  	// Check if section exists.
   382  	if _, ok := c.data[section]; !ok {
   383  		return false
   384  	}
   385  
   386  	delete(c.data, section)
   387  	// Remove comments of section.
   388  	c.SetSectionComments(section, "")
   389  	// Get index of section.
   390  	i := 0
   391  	for _, secName := range c.sectionList {
   392  		if secName == section {
   393  			break
   394  		}
   395  		i++
   396  	}
   397  	// Remove from section and key list.
   398  	c.sectionList = append(c.sectionList[:i], c.sectionList[i+1:]...)
   399  	delete(c.keyList, section)
   400  	return true
   401  }
   402  
   403  // GetSection returns key-value pairs in given section.
   404  // It section does not exist, returns nil and error.
   405  func (c *ConfigFile) GetSection(section string) (map[string]string, error) {
   406  	// Blank section name represents DEFAULT section.
   407  	if len(section) == 0 {
   408  		section = DEFAULT_SECTION
   409  	}
   410  
   411  	// Check if section exists.
   412  	if _, ok := c.data[section]; !ok {
   413  		// Section does not exist.
   414  		return nil, getError{ErrSectionNotFound, section}
   415  	}
   416  
   417  	// Remove pre-defined key.
   418  	secMap := c.data[section]
   419  	delete(c.data[section], " ")
   420  
   421  	// Section exists.
   422  	return secMap, nil
   423  }
   424  
   425  // SetSectionComments adds new section comments to the configuration.
   426  // If comments are empty(0 length), it will remove its section comments!
   427  // It returns true if the comments were inserted or removed,
   428  // or returns false if the comments were overwritten.
   429  func (c *ConfigFile) SetSectionComments(section, comments string) bool {
   430  	// Blank section name represents DEFAULT section.
   431  	if len(section) == 0 {
   432  		section = DEFAULT_SECTION
   433  	}
   434  
   435  	if len(comments) == 0 {
   436  		if _, ok := c.sectionComments[section]; ok {
   437  			delete(c.sectionComments, section)
   438  		}
   439  
   440  		// Not exists can be seen as remove.
   441  		return true
   442  	}
   443  
   444  	// Check if comments exists.
   445  	_, ok := c.sectionComments[section]
   446  	if comments[0] != '#' && comments[0] != ';' {
   447  		comments = "; " + comments
   448  	}
   449  	c.sectionComments[section] = comments
   450  	return !ok
   451  }
   452  
   453  // SetKeyComments adds new section-key comments to the configuration.
   454  // If comments are empty(0 length), it will remove its section-key comments!
   455  // It returns true if the comments were inserted or removed,
   456  // or returns false if the comments were overwritten.
   457  // If the section does not exist in advance, it is created.
   458  func (c *ConfigFile) SetKeyComments(section, key, comments string) bool {
   459  	// Blank section name represents DEFAULT section.
   460  	if len(section) == 0 {
   461  		section = DEFAULT_SECTION
   462  	}
   463  
   464  	// Check if section exists.
   465  	if _, ok := c.keyComments[section]; ok {
   466  		if len(comments) == 0 {
   467  			if _, ok := c.keyComments[section][key]; ok {
   468  				delete(c.keyComments[section], key)
   469  			}
   470  
   471  			// Not exists can be seen as remove.
   472  			return true
   473  		}
   474  	} else {
   475  		if len(comments) == 0 {
   476  			// Not exists can be seen as remove.
   477  			return true
   478  		} else {
   479  			// Execute add operation.
   480  			c.keyComments[section] = make(map[string]string)
   481  		}
   482  	}
   483  
   484  	// Check if key exists.
   485  	_, ok := c.keyComments[section][key]
   486  	if comments[0] != '#' && comments[0] != ';' {
   487  		comments = "; " + comments
   488  	}
   489  	c.keyComments[section][key] = comments
   490  	return !ok
   491  }
   492  
   493  // GetSectionComments returns the comments in the given section.
   494  // It returns an empty string(0 length) if the comments do not exist.
   495  func (c *ConfigFile) GetSectionComments(section string) (comments string) {
   496  	// Blank section name represents DEFAULT section.
   497  	if len(section) == 0 {
   498  		section = DEFAULT_SECTION
   499  	}
   500  	return c.sectionComments[section]
   501  }
   502  
   503  // GetKeyComments returns the comments of key in the given section.
   504  // It returns an empty string(0 length) if the comments do not exist.
   505  func (c *ConfigFile) GetKeyComments(section, key string) (comments string) {
   506  	// Blank section name represents DEFAULT section.
   507  	if len(section) == 0 {
   508  		section = DEFAULT_SECTION
   509  	}
   510  
   511  	if _, ok := c.keyComments[section]; ok {
   512  		return c.keyComments[section][key]
   513  	}
   514  	return ""
   515  }
   516  
   517  // getError occurs when get value in configuration file with invalid parameter.
   518  type getError struct {
   519  	Reason int
   520  	Name   string
   521  }
   522  
   523  // Error implements Error interface.
   524  func (err getError) Error() string {
   525  	switch err.Reason {
   526  	case ErrSectionNotFound:
   527  		return fmt.Sprintf("section '%s' not found", err.Name)
   528  	case ErrKeyNotFound:
   529  		return fmt.Sprintf("key '%s' not found", err.Name)
   530  	}
   531  	return "invalid get error"
   532  }