...

Source file src/edge-infra.dev/pkg/lib/ini/ini.go

Documentation: edge-infra.dev/pkg/lib/ini

     1  // Package ini provides INI file read and write functionality in Go.
     2  package ini
     3  
     4  import (
     5  	"os"
     6  	"regexp"
     7  	"runtime"
     8  	"strings"
     9  )
    10  
    11  const (
    12  	// Maximum allowed depth when recursively substituing variable names.
    13  	depthValues = 99
    14  )
    15  
    16  var (
    17  	// DefaultSection is the name of default section. You can use this var or the string literal.
    18  	// In most of cases, an empty string is all you need to access the section.
    19  	DefaultSection = "DEFAULT"
    20  
    21  	// LineBreak is the delimiter to determine or compose a new line.
    22  	// This variable will be changed to "\r\n" automatically on Windows at package init time.
    23  	LineBreak = "\n"
    24  
    25  	// Variable regexp pattern: %(variable)s
    26  	varPattern = regexp.MustCompile(`%\(([^)]+)\)s`)
    27  
    28  	// DefaultHeader explicitly writes default section header.
    29  	DefaultHeader = false
    30  
    31  	// PrettySection indicates whether to put a line between sections.
    32  	PrettySection = true
    33  	// PrettyFormat indicates whether to align "=" sign with spaces to produce pretty output
    34  	// or reduce all possible spaces for compact format.
    35  	PrettyFormat = true
    36  	// PrettyEqual places spaces around "=" sign even when PrettyFormat is false.
    37  	PrettyEqual = false
    38  	// DefaultFormatLeft places custom spaces on the left when PrettyFormat and PrettyEqual are both disabled.
    39  	DefaultFormatLeft = ""
    40  	// DefaultFormatRight places custom spaces on the right when PrettyFormat and PrettyEqual are both disabled.
    41  	DefaultFormatRight = ""
    42  )
    43  
    44  var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test")
    45  
    46  func init() {
    47  	if runtime.GOOS == "windows" && !inTest {
    48  		LineBreak = "\r\n"
    49  	}
    50  }
    51  
    52  // LoadOptions contains all customized options used for load data source(s).
    53  type LoadOptions struct {
    54  	// Loose indicates whether the parser should ignore nonexistent files or return error.
    55  	Loose bool
    56  	// Insensitive indicates whether the parser forces all section and key names to lowercase.
    57  	Insensitive bool
    58  	// InsensitiveSections indicates whether the parser forces all section to lowercase.
    59  	InsensitiveSections bool
    60  	// InsensitiveKeys indicates whether the parser forces all key names to lowercase.
    61  	InsensitiveKeys bool
    62  	// IgnoreContinuation indicates whether to ignore continuation lines while parsing.
    63  	IgnoreContinuation bool
    64  	// IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value.
    65  	IgnoreInlineComment bool
    66  	// SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs.
    67  	SkipUnrecognizableLines bool
    68  	// ShortCircuit indicates whether to ignore other configuration sources after loaded the first available configuration source.
    69  	ShortCircuit bool
    70  	// AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
    71  	// This type of keys are mostly used in my.cnf.
    72  	AllowBooleanKeys bool
    73  	// AllowShadows indicates whether to keep track of keys with same name under same section.
    74  	AllowShadows bool
    75  	// AllowNestedValues indicates whether to allow AWS-like nested values.
    76  	// Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values
    77  	AllowNestedValues bool
    78  	// AllowPythonMultilineValues indicates whether to allow Python-like multi-line values.
    79  	// Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure
    80  	// Relevant quote:  Values can also span multiple lines, as long as they are indented deeper
    81  	// than the first line of the value.
    82  	AllowPythonMultilineValues bool
    83  	// SpaceBeforeInlineComment indicates whether to allow comment symbols (\# and \;) inside value.
    84  	// Docs: https://docs.python.org/2/library/configparser.html
    85  	// Quote: Comments may appear on their own in an otherwise empty line, or may be entered in lines holding values or section names.
    86  	// In the latter case, they need to be preceded by a whitespace character to be recognized as a comment.
    87  	SpaceBeforeInlineComment bool
    88  	// UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format
    89  	// when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value"
    90  	UnescapeValueDoubleQuotes bool
    91  	// UnescapeValueCommentSymbols indicates to unescape comment symbols (\# and \;) inside value to regular format
    92  	// when value is NOT surrounded by any quotes.
    93  	// Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all.
    94  	UnescapeValueCommentSymbols bool
    95  	// UnparseableSections stores a list of blocks that are allowed with raw content which do not otherwise
    96  	// conform to key/value pairs. Specify the names of those blocks here.
    97  	UnparseableSections []string
    98  	// KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:".
    99  	KeyValueDelimiters string
   100  	// KeyValueDelimiterOnWrite is the delimiter that are used to separate key and value output. By default, it is "=".
   101  	KeyValueDelimiterOnWrite string
   102  	// ChildSectionDelimiter is the delimiter that is used to separate child sections. By default, it is ".".
   103  	ChildSectionDelimiter string
   104  	// PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes).
   105  	PreserveSurroundedQuote bool
   106  	// DebugFunc is called to collect debug information (currently only useful to debug parsing Python-style multiline values).
   107  	DebugFunc DebugFunc
   108  	// ReaderBufferSize is the buffer size of the reader in bytes.
   109  	ReaderBufferSize int
   110  	// AllowNonUniqueSections indicates whether to allow sections with the same name multiple times.
   111  	AllowNonUniqueSections bool
   112  	// AllowDuplicateShadowValues indicates whether values for shadowed keys should be deduplicated.
   113  	AllowDuplicateShadowValues bool
   114  }
   115  
   116  // DebugFunc is the type of function called to log parse events.
   117  type DebugFunc func(message string)
   118  
   119  // LoadSources allows caller to apply customized options for loading from data source(s).
   120  func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) {
   121  	sources := make([]dataSource, len(others)+1)
   122  	sources[0], err = parseDataSource(source)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	for i := range others {
   127  		sources[i+1], err = parseDataSource(others[i])
   128  		if err != nil {
   129  			return nil, err
   130  		}
   131  	}
   132  	f := newFile(sources, opts)
   133  	if err = f.Reload(); err != nil {
   134  		return nil, err
   135  	}
   136  	return f, nil
   137  }
   138  
   139  // Load loads and parses from INI data sources.
   140  // Arguments can be mixed of file name with string type, or raw data in []byte.
   141  // It will return error if list contains nonexistent files.
   142  func Load(source interface{}, others ...interface{}) (*File, error) {
   143  	return LoadSources(LoadOptions{}, source, others...)
   144  }
   145  
   146  // LooseLoad has exactly same functionality as Load function
   147  // except it ignores nonexistent files instead of returning error.
   148  func LooseLoad(source interface{}, others ...interface{}) (*File, error) {
   149  	return LoadSources(LoadOptions{Loose: true}, source, others...)
   150  }
   151  
   152  // InsensitiveLoad has exactly same functionality as Load function
   153  // except it forces all section and key names to be lowercased.
   154  func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) {
   155  	return LoadSources(LoadOptions{Insensitive: true}, source, others...)
   156  }
   157  
   158  // ShadowLoad has exactly same functionality as Load function
   159  // except it allows have shadow keys.
   160  func ShadowLoad(source interface{}, others ...interface{}) (*File, error) {
   161  	return LoadSources(LoadOptions{AllowShadows: true}, source, others...)
   162  }
   163  

View as plain text