Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
songlinfeng
container-toolkit
Commits
ae831b78
Commit
ae831b78
authored
Oct 28, 2025
by
songlinfeng
Browse files
add support podman
parent
3e3a4e74
Changes
24
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
1526 additions
and
0 deletions
+1526
-0
vendor/gopkg.in/ini.v1/parser.go
vendor/gopkg.in/ini.v1/parser.go
+520
-0
vendor/gopkg.in/ini.v1/section.go
vendor/gopkg.in/ini.v1/section.go
+256
-0
vendor/gopkg.in/ini.v1/struct.go
vendor/gopkg.in/ini.v1/struct.go
+747
-0
vendor/modules.txt
vendor/modules.txt
+3
-0
No files found.
vendor/gopkg.in/ini.v1/parser.go
0 → 100644
View file @
ae831b78
// Copyright 2015 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package
ini
import
(
"bufio"
"bytes"
"fmt"
"io"
"regexp"
"strconv"
"strings"
"unicode"
)
const
minReaderBufferSize
=
4096
var
pythonMultiline
=
regexp
.
MustCompile
(
`^([\t\f ]+)(.*)`
)
type
parserOptions
struct
{
IgnoreContinuation
bool
IgnoreInlineComment
bool
AllowPythonMultilineValues
bool
SpaceBeforeInlineComment
bool
UnescapeValueDoubleQuotes
bool
UnescapeValueCommentSymbols
bool
PreserveSurroundedQuote
bool
DebugFunc
DebugFunc
ReaderBufferSize
int
}
type
parser
struct
{
buf
*
bufio
.
Reader
options
parserOptions
isEOF
bool
count
int
comment
*
bytes
.
Buffer
}
func
(
p
*
parser
)
debug
(
format
string
,
args
...
interface
{})
{
if
p
.
options
.
DebugFunc
!=
nil
{
p
.
options
.
DebugFunc
(
fmt
.
Sprintf
(
format
,
args
...
))
}
}
func
newParser
(
r
io
.
Reader
,
opts
parserOptions
)
*
parser
{
size
:=
opts
.
ReaderBufferSize
if
size
<
minReaderBufferSize
{
size
=
minReaderBufferSize
}
return
&
parser
{
buf
:
bufio
.
NewReaderSize
(
r
,
size
),
options
:
opts
,
count
:
1
,
comment
:
&
bytes
.
Buffer
{},
}
}
// BOM handles header of UTF-8, UTF-16 LE and UTF-16 BE's BOM format.
// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
func
(
p
*
parser
)
BOM
()
error
{
mask
,
err
:=
p
.
buf
.
Peek
(
2
)
if
err
!=
nil
&&
err
!=
io
.
EOF
{
return
err
}
else
if
len
(
mask
)
<
2
{
return
nil
}
switch
{
case
mask
[
0
]
==
254
&&
mask
[
1
]
==
255
:
fallthrough
case
mask
[
0
]
==
255
&&
mask
[
1
]
==
254
:
_
,
err
=
p
.
buf
.
Read
(
mask
)
if
err
!=
nil
{
return
err
}
case
mask
[
0
]
==
239
&&
mask
[
1
]
==
187
:
mask
,
err
:=
p
.
buf
.
Peek
(
3
)
if
err
!=
nil
&&
err
!=
io
.
EOF
{
return
err
}
else
if
len
(
mask
)
<
3
{
return
nil
}
if
mask
[
2
]
==
191
{
_
,
err
=
p
.
buf
.
Read
(
mask
)
if
err
!=
nil
{
return
err
}
}
}
return
nil
}
func
(
p
*
parser
)
readUntil
(
delim
byte
)
([]
byte
,
error
)
{
data
,
err
:=
p
.
buf
.
ReadBytes
(
delim
)
if
err
!=
nil
{
if
err
==
io
.
EOF
{
p
.
isEOF
=
true
}
else
{
return
nil
,
err
}
}
return
data
,
nil
}
func
cleanComment
(
in
[]
byte
)
([]
byte
,
bool
)
{
i
:=
bytes
.
IndexAny
(
in
,
"#;"
)
if
i
==
-
1
{
return
nil
,
false
}
return
in
[
i
:
],
true
}
func
readKeyName
(
delimiters
string
,
in
[]
byte
)
(
string
,
int
,
error
)
{
line
:=
string
(
in
)
// Check if key name surrounded by quotes.
var
keyQuote
string
if
line
[
0
]
==
'"'
{
if
len
(
line
)
>
6
&&
line
[
0
:
3
]
==
`"""`
{
keyQuote
=
`"""`
}
else
{
keyQuote
=
`"`
}
}
else
if
line
[
0
]
==
'`'
{
keyQuote
=
"`"
}
// Get out key name
var
endIdx
int
if
len
(
keyQuote
)
>
0
{
startIdx
:=
len
(
keyQuote
)
// FIXME: fail case -> """"""name"""=value
pos
:=
strings
.
Index
(
line
[
startIdx
:
],
keyQuote
)
if
pos
==
-
1
{
return
""
,
-
1
,
fmt
.
Errorf
(
"missing closing key quote: %s"
,
line
)
}
pos
+=
startIdx
// Find key-value delimiter
i
:=
strings
.
IndexAny
(
line
[
pos
+
startIdx
:
],
delimiters
)
if
i
<
0
{
return
""
,
-
1
,
ErrDelimiterNotFound
{
line
}
}
endIdx
=
pos
+
i
return
strings
.
TrimSpace
(
line
[
startIdx
:
pos
]),
endIdx
+
startIdx
+
1
,
nil
}
endIdx
=
strings
.
IndexAny
(
line
,
delimiters
)
if
endIdx
<
0
{
return
""
,
-
1
,
ErrDelimiterNotFound
{
line
}
}
if
endIdx
==
0
{
return
""
,
-
1
,
ErrEmptyKeyName
{
line
}
}
return
strings
.
TrimSpace
(
line
[
0
:
endIdx
]),
endIdx
+
1
,
nil
}
func
(
p
*
parser
)
readMultilines
(
line
,
val
,
valQuote
string
)
(
string
,
error
)
{
for
{
data
,
err
:=
p
.
readUntil
(
'\n'
)
if
err
!=
nil
{
return
""
,
err
}
next
:=
string
(
data
)
pos
:=
strings
.
LastIndex
(
next
,
valQuote
)
if
pos
>
-
1
{
val
+=
next
[
:
pos
]
comment
,
has
:=
cleanComment
([]
byte
(
next
[
pos
:
]))
if
has
{
p
.
comment
.
Write
(
bytes
.
TrimSpace
(
comment
))
}
break
}
val
+=
next
if
p
.
isEOF
{
return
""
,
fmt
.
Errorf
(
"missing closing key quote from %q to %q"
,
line
,
next
)
}
}
return
val
,
nil
}
func
(
p
*
parser
)
readContinuationLines
(
val
string
)
(
string
,
error
)
{
for
{
data
,
err
:=
p
.
readUntil
(
'\n'
)
if
err
!=
nil
{
return
""
,
err
}
next
:=
strings
.
TrimSpace
(
string
(
data
))
if
len
(
next
)
==
0
{
break
}
val
+=
next
if
val
[
len
(
val
)
-
1
]
!=
'\\'
{
break
}
val
=
val
[
:
len
(
val
)
-
1
]
}
return
val
,
nil
}
// hasSurroundedQuote check if and only if the first and last characters
// are quotes \" or \'.
// It returns false if any other parts also contain same kind of quotes.
func
hasSurroundedQuote
(
in
string
,
quote
byte
)
bool
{
return
len
(
in
)
>=
2
&&
in
[
0
]
==
quote
&&
in
[
len
(
in
)
-
1
]
==
quote
&&
strings
.
IndexByte
(
in
[
1
:
],
quote
)
==
len
(
in
)
-
2
}
func
(
p
*
parser
)
readValue
(
in
[]
byte
,
bufferSize
int
)
(
string
,
error
)
{
line
:=
strings
.
TrimLeftFunc
(
string
(
in
),
unicode
.
IsSpace
)
if
len
(
line
)
==
0
{
if
p
.
options
.
AllowPythonMultilineValues
&&
len
(
in
)
>
0
&&
in
[
len
(
in
)
-
1
]
==
'\n'
{
return
p
.
readPythonMultilines
(
line
,
bufferSize
)
}
return
""
,
nil
}
var
valQuote
string
if
len
(
line
)
>
3
&&
line
[
0
:
3
]
==
`"""`
{
valQuote
=
`"""`
}
else
if
line
[
0
]
==
'`'
{
valQuote
=
"`"
}
else
if
p
.
options
.
UnescapeValueDoubleQuotes
&&
line
[
0
]
==
'"'
{
valQuote
=
`"`
}
if
len
(
valQuote
)
>
0
{
startIdx
:=
len
(
valQuote
)
pos
:=
strings
.
LastIndex
(
line
[
startIdx
:
],
valQuote
)
// Check for multi-line value
if
pos
==
-
1
{
return
p
.
readMultilines
(
line
,
line
[
startIdx
:
],
valQuote
)
}
if
p
.
options
.
UnescapeValueDoubleQuotes
&&
valQuote
==
`"`
{
return
strings
.
Replace
(
line
[
startIdx
:
pos
+
startIdx
],
`\"`
,
`"`
,
-
1
),
nil
}
return
line
[
startIdx
:
pos
+
startIdx
],
nil
}
lastChar
:=
line
[
len
(
line
)
-
1
]
// Won't be able to reach here if value only contains whitespace
line
=
strings
.
TrimSpace
(
line
)
trimmedLastChar
:=
line
[
len
(
line
)
-
1
]
// Check continuation lines when desired
if
!
p
.
options
.
IgnoreContinuation
&&
trimmedLastChar
==
'\\'
{
return
p
.
readContinuationLines
(
line
[
:
len
(
line
)
-
1
])
}
// Check if ignore inline comment
if
!
p
.
options
.
IgnoreInlineComment
{
var
i
int
if
p
.
options
.
SpaceBeforeInlineComment
{
i
=
strings
.
Index
(
line
,
" #"
)
if
i
==
-
1
{
i
=
strings
.
Index
(
line
,
" ;"
)
}
}
else
{
i
=
strings
.
IndexAny
(
line
,
"#;"
)
}
if
i
>
-
1
{
p
.
comment
.
WriteString
(
line
[
i
:
])
line
=
strings
.
TrimSpace
(
line
[
:
i
])
}
}
// Trim single and double quotes
if
(
hasSurroundedQuote
(
line
,
'\'
'
)
||
hasSurroundedQuote
(
line
,
'"'
))
&&
!
p
.
options
.
PreserveSurroundedQuote
{
line
=
line
[
1
:
len
(
line
)
-
1
]
}
else
if
len
(
valQuote
)
==
0
&&
p
.
options
.
UnescapeValueCommentSymbols
{
line
=
strings
.
ReplaceAll
(
line
,
`\;`
,
";"
)
line
=
strings
.
ReplaceAll
(
line
,
`\#`
,
"#"
)
}
else
if
p
.
options
.
AllowPythonMultilineValues
&&
lastChar
==
'\n'
{
return
p
.
readPythonMultilines
(
line
,
bufferSize
)
}
return
line
,
nil
}
func
(
p
*
parser
)
readPythonMultilines
(
line
string
,
bufferSize
int
)
(
string
,
error
)
{
parserBufferPeekResult
,
_
:=
p
.
buf
.
Peek
(
bufferSize
)
peekBuffer
:=
bytes
.
NewBuffer
(
parserBufferPeekResult
)
for
{
peekData
,
peekErr
:=
peekBuffer
.
ReadBytes
(
'\n'
)
if
peekErr
!=
nil
&&
peekErr
!=
io
.
EOF
{
p
.
debug
(
"readPythonMultilines: failed to peek with error: %v"
,
peekErr
)
return
""
,
peekErr
}
p
.
debug
(
"readPythonMultilines: parsing %q"
,
string
(
peekData
))
peekMatches
:=
pythonMultiline
.
FindStringSubmatch
(
string
(
peekData
))
p
.
debug
(
"readPythonMultilines: matched %d parts"
,
len
(
peekMatches
))
for
n
,
v
:=
range
peekMatches
{
p
.
debug
(
" %d: %q"
,
n
,
v
)
}
// Return if not a Python multiline value.
if
len
(
peekMatches
)
!=
3
{
p
.
debug
(
"readPythonMultilines: end of value, got: %q"
,
line
)
return
line
,
nil
}
// Advance the parser reader (buffer) in-sync with the peek buffer.
_
,
err
:=
p
.
buf
.
Discard
(
len
(
peekData
))
if
err
!=
nil
{
p
.
debug
(
"readPythonMultilines: failed to skip to the end, returning error"
)
return
""
,
err
}
line
+=
"
\n
"
+
peekMatches
[
0
]
}
}
// parse parses data through an io.Reader.
func
(
f
*
File
)
parse
(
reader
io
.
Reader
)
(
err
error
)
{
p
:=
newParser
(
reader
,
parserOptions
{
IgnoreContinuation
:
f
.
options
.
IgnoreContinuation
,
IgnoreInlineComment
:
f
.
options
.
IgnoreInlineComment
,
AllowPythonMultilineValues
:
f
.
options
.
AllowPythonMultilineValues
,
SpaceBeforeInlineComment
:
f
.
options
.
SpaceBeforeInlineComment
,
UnescapeValueDoubleQuotes
:
f
.
options
.
UnescapeValueDoubleQuotes
,
UnescapeValueCommentSymbols
:
f
.
options
.
UnescapeValueCommentSymbols
,
PreserveSurroundedQuote
:
f
.
options
.
PreserveSurroundedQuote
,
DebugFunc
:
f
.
options
.
DebugFunc
,
ReaderBufferSize
:
f
.
options
.
ReaderBufferSize
,
})
if
err
=
p
.
BOM
();
err
!=
nil
{
return
fmt
.
Errorf
(
"BOM: %v"
,
err
)
}
// Ignore error because default section name is never empty string.
name
:=
DefaultSection
if
f
.
options
.
Insensitive
||
f
.
options
.
InsensitiveSections
{
name
=
strings
.
ToLower
(
DefaultSection
)
}
section
,
_
:=
f
.
NewSection
(
name
)
// This "last" is not strictly equivalent to "previous one" if current key is not the first nested key
var
isLastValueEmpty
bool
var
lastRegularKey
*
Key
var
line
[]
byte
var
inUnparseableSection
bool
// NOTE: Iterate and increase `currentPeekSize` until
// the size of the parser buffer is found.
// TODO(unknwon): When Golang 1.10 is the lowest version supported, replace with `parserBufferSize := p.buf.Size()`.
parserBufferSize
:=
0
// NOTE: Peek 4kb at a time.
currentPeekSize
:=
minReaderBufferSize
if
f
.
options
.
AllowPythonMultilineValues
{
for
{
peekBytes
,
_
:=
p
.
buf
.
Peek
(
currentPeekSize
)
peekBytesLength
:=
len
(
peekBytes
)
if
parserBufferSize
>=
peekBytesLength
{
break
}
currentPeekSize
*=
2
parserBufferSize
=
peekBytesLength
}
}
for
!
p
.
isEOF
{
line
,
err
=
p
.
readUntil
(
'\n'
)
if
err
!=
nil
{
return
err
}
if
f
.
options
.
AllowNestedValues
&&
isLastValueEmpty
&&
len
(
line
)
>
0
{
if
line
[
0
]
==
' '
||
line
[
0
]
==
'\t'
{
err
=
lastRegularKey
.
addNestedValue
(
string
(
bytes
.
TrimSpace
(
line
)))
if
err
!=
nil
{
return
err
}
continue
}
}
line
=
bytes
.
TrimLeftFunc
(
line
,
unicode
.
IsSpace
)
if
len
(
line
)
==
0
{
continue
}
// Comments
if
line
[
0
]
==
'#'
||
line
[
0
]
==
';'
{
// Note: we do not care ending line break,
// it is needed for adding second line,
// so just clean it once at the end when set to value.
p
.
comment
.
Write
(
line
)
continue
}
// Section
if
line
[
0
]
==
'['
{
// Read to the next ']' (TODO: support quoted strings)
closeIdx
:=
bytes
.
LastIndexByte
(
line
,
']'
)
if
closeIdx
==
-
1
{
return
fmt
.
Errorf
(
"unclosed section: %s"
,
line
)
}
name
:=
string
(
line
[
1
:
closeIdx
])
section
,
err
=
f
.
NewSection
(
name
)
if
err
!=
nil
{
return
err
}
comment
,
has
:=
cleanComment
(
line
[
closeIdx
+
1
:
])
if
has
{
p
.
comment
.
Write
(
comment
)
}
section
.
Comment
=
strings
.
TrimSpace
(
p
.
comment
.
String
())
// Reset auto-counter and comments
p
.
comment
.
Reset
()
p
.
count
=
1
// Nested values can't span sections
isLastValueEmpty
=
false
inUnparseableSection
=
false
for
i
:=
range
f
.
options
.
UnparseableSections
{
if
f
.
options
.
UnparseableSections
[
i
]
==
name
||
((
f
.
options
.
Insensitive
||
f
.
options
.
InsensitiveSections
)
&&
strings
.
EqualFold
(
f
.
options
.
UnparseableSections
[
i
],
name
))
{
inUnparseableSection
=
true
continue
}
}
continue
}
if
inUnparseableSection
{
section
.
isRawSection
=
true
section
.
rawBody
+=
string
(
line
)
continue
}
kname
,
offset
,
err
:=
readKeyName
(
f
.
options
.
KeyValueDelimiters
,
line
)
if
err
!=
nil
{
switch
{
// Treat as boolean key when desired, and whole line is key name.
case
IsErrDelimiterNotFound
(
err
)
:
switch
{
case
f
.
options
.
AllowBooleanKeys
:
kname
,
err
:=
p
.
readValue
(
line
,
parserBufferSize
)
if
err
!=
nil
{
return
err
}
key
,
err
:=
section
.
NewBooleanKey
(
kname
)
if
err
!=
nil
{
return
err
}
key
.
Comment
=
strings
.
TrimSpace
(
p
.
comment
.
String
())
p
.
comment
.
Reset
()
continue
case
f
.
options
.
SkipUnrecognizableLines
:
continue
}
case
IsErrEmptyKeyName
(
err
)
&&
f
.
options
.
SkipUnrecognizableLines
:
continue
}
return
err
}
// Auto increment.
isAutoIncr
:=
false
if
kname
==
"-"
{
isAutoIncr
=
true
kname
=
"#"
+
strconv
.
Itoa
(
p
.
count
)
p
.
count
++
}
value
,
err
:=
p
.
readValue
(
line
[
offset
:
],
parserBufferSize
)
if
err
!=
nil
{
return
err
}
isLastValueEmpty
=
len
(
value
)
==
0
key
,
err
:=
section
.
NewKey
(
kname
,
value
)
if
err
!=
nil
{
return
err
}
key
.
isAutoIncrement
=
isAutoIncr
key
.
Comment
=
strings
.
TrimSpace
(
p
.
comment
.
String
())
p
.
comment
.
Reset
()
lastRegularKey
=
key
}
return
nil
}
vendor/gopkg.in/ini.v1/section.go
0 → 100644
View file @
ae831b78
// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package
ini
import
(
"errors"
"fmt"
"strings"
)
// Section represents a config section.
type
Section
struct
{
f
*
File
Comment
string
name
string
keys
map
[
string
]
*
Key
keyList
[]
string
keysHash
map
[
string
]
string
isRawSection
bool
rawBody
string
}
func
newSection
(
f
*
File
,
name
string
)
*
Section
{
return
&
Section
{
f
:
f
,
name
:
name
,
keys
:
make
(
map
[
string
]
*
Key
),
keyList
:
make
([]
string
,
0
,
10
),
keysHash
:
make
(
map
[
string
]
string
),
}
}
// Name returns name of Section.
func
(
s
*
Section
)
Name
()
string
{
return
s
.
name
}
// Body returns rawBody of Section if the section was marked as unparseable.
// It still follows the other rules of the INI format surrounding leading/trailing whitespace.
func
(
s
*
Section
)
Body
()
string
{
return
strings
.
TrimSpace
(
s
.
rawBody
)
}
// SetBody updates body content only if section is raw.
func
(
s
*
Section
)
SetBody
(
body
string
)
{
if
!
s
.
isRawSection
{
return
}
s
.
rawBody
=
body
}
// NewKey creates a new key to given section.
func
(
s
*
Section
)
NewKey
(
name
,
val
string
)
(
*
Key
,
error
)
{
if
len
(
name
)
==
0
{
return
nil
,
errors
.
New
(
"error creating new key: empty key name"
)
}
else
if
s
.
f
.
options
.
Insensitive
||
s
.
f
.
options
.
InsensitiveKeys
{
name
=
strings
.
ToLower
(
name
)
}
if
s
.
f
.
BlockMode
{
s
.
f
.
lock
.
Lock
()
defer
s
.
f
.
lock
.
Unlock
()
}
if
inSlice
(
name
,
s
.
keyList
)
{
if
s
.
f
.
options
.
AllowShadows
{
if
err
:=
s
.
keys
[
name
]
.
addShadow
(
val
);
err
!=
nil
{
return
nil
,
err
}
}
else
{
s
.
keys
[
name
]
.
value
=
val
s
.
keysHash
[
name
]
=
val
}
return
s
.
keys
[
name
],
nil
}
s
.
keyList
=
append
(
s
.
keyList
,
name
)
s
.
keys
[
name
]
=
newKey
(
s
,
name
,
val
)
s
.
keysHash
[
name
]
=
val
return
s
.
keys
[
name
],
nil
}
// NewBooleanKey creates a new boolean type key to given section.
func
(
s
*
Section
)
NewBooleanKey
(
name
string
)
(
*
Key
,
error
)
{
key
,
err
:=
s
.
NewKey
(
name
,
"true"
)
if
err
!=
nil
{
return
nil
,
err
}
key
.
isBooleanType
=
true
return
key
,
nil
}
// GetKey returns key in section by given name.
func
(
s
*
Section
)
GetKey
(
name
string
)
(
*
Key
,
error
)
{
if
s
.
f
.
BlockMode
{
s
.
f
.
lock
.
RLock
()
}
if
s
.
f
.
options
.
Insensitive
||
s
.
f
.
options
.
InsensitiveKeys
{
name
=
strings
.
ToLower
(
name
)
}
key
:=
s
.
keys
[
name
]
if
s
.
f
.
BlockMode
{
s
.
f
.
lock
.
RUnlock
()
}
if
key
==
nil
{
// Check if it is a child-section.
sname
:=
s
.
name
for
{
if
i
:=
strings
.
LastIndex
(
sname
,
s
.
f
.
options
.
ChildSectionDelimiter
);
i
>
-
1
{
sname
=
sname
[
:
i
]
sec
,
err
:=
s
.
f
.
GetSection
(
sname
)
if
err
!=
nil
{
continue
}
return
sec
.
GetKey
(
name
)
}
break
}
return
nil
,
fmt
.
Errorf
(
"error when getting key of section %q: key %q not exists"
,
s
.
name
,
name
)
}
return
key
,
nil
}
// HasKey returns true if section contains a key with given name.
func
(
s
*
Section
)
HasKey
(
name
string
)
bool
{
key
,
_
:=
s
.
GetKey
(
name
)
return
key
!=
nil
}
// Deprecated: Use "HasKey" instead.
func
(
s
*
Section
)
Haskey
(
name
string
)
bool
{
return
s
.
HasKey
(
name
)
}
// HasValue returns true if section contains given raw value.
func
(
s
*
Section
)
HasValue
(
value
string
)
bool
{
if
s
.
f
.
BlockMode
{
s
.
f
.
lock
.
RLock
()
defer
s
.
f
.
lock
.
RUnlock
()
}
for
_
,
k
:=
range
s
.
keys
{
if
value
==
k
.
value
{
return
true
}
}
return
false
}
// Key assumes named Key exists in section and returns a zero-value when not.
func
(
s
*
Section
)
Key
(
name
string
)
*
Key
{
key
,
err
:=
s
.
GetKey
(
name
)
if
err
!=
nil
{
// It's OK here because the only possible error is empty key name,
// but if it's empty, this piece of code won't be executed.
key
,
_
=
s
.
NewKey
(
name
,
""
)
return
key
}
return
key
}
// Keys returns list of keys of section.
func
(
s
*
Section
)
Keys
()
[]
*
Key
{
keys
:=
make
([]
*
Key
,
len
(
s
.
keyList
))
for
i
:=
range
s
.
keyList
{
keys
[
i
]
=
s
.
Key
(
s
.
keyList
[
i
])
}
return
keys
}
// ParentKeys returns list of keys of parent section.
func
(
s
*
Section
)
ParentKeys
()
[]
*
Key
{
var
parentKeys
[]
*
Key
sname
:=
s
.
name
for
{
if
i
:=
strings
.
LastIndex
(
sname
,
s
.
f
.
options
.
ChildSectionDelimiter
);
i
>
-
1
{
sname
=
sname
[
:
i
]
sec
,
err
:=
s
.
f
.
GetSection
(
sname
)
if
err
!=
nil
{
continue
}
parentKeys
=
append
(
parentKeys
,
sec
.
Keys
()
...
)
}
else
{
break
}
}
return
parentKeys
}
// KeyStrings returns list of key names of section.
func
(
s
*
Section
)
KeyStrings
()
[]
string
{
list
:=
make
([]
string
,
len
(
s
.
keyList
))
copy
(
list
,
s
.
keyList
)
return
list
}
// KeysHash returns keys hash consisting of names and values.
func
(
s
*
Section
)
KeysHash
()
map
[
string
]
string
{
if
s
.
f
.
BlockMode
{
s
.
f
.
lock
.
RLock
()
defer
s
.
f
.
lock
.
RUnlock
()
}
hash
:=
make
(
map
[
string
]
string
,
len
(
s
.
keysHash
))
for
key
,
value
:=
range
s
.
keysHash
{
hash
[
key
]
=
value
}
return
hash
}
// DeleteKey deletes a key from section.
func
(
s
*
Section
)
DeleteKey
(
name
string
)
{
if
s
.
f
.
BlockMode
{
s
.
f
.
lock
.
Lock
()
defer
s
.
f
.
lock
.
Unlock
()
}
for
i
,
k
:=
range
s
.
keyList
{
if
k
==
name
{
s
.
keyList
=
append
(
s
.
keyList
[
:
i
],
s
.
keyList
[
i
+
1
:
]
...
)
delete
(
s
.
keys
,
name
)
delete
(
s
.
keysHash
,
name
)
return
}
}
}
// ChildSections returns a list of child sections of current section.
// For example, "[parent.child1]" and "[parent.child12]" are child sections
// of section "[parent]".
func
(
s
*
Section
)
ChildSections
()
[]
*
Section
{
prefix
:=
s
.
name
+
s
.
f
.
options
.
ChildSectionDelimiter
children
:=
make
([]
*
Section
,
0
,
3
)
for
_
,
name
:=
range
s
.
f
.
sectionList
{
if
strings
.
HasPrefix
(
name
,
prefix
)
{
children
=
append
(
children
,
s
.
f
.
sections
[
name
]
...
)
}
}
return
children
}
vendor/gopkg.in/ini.v1/struct.go
0 → 100644
View file @
ae831b78
// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package
ini
import
(
"bytes"
"errors"
"fmt"
"reflect"
"strings"
"time"
"unicode"
)
// NameMapper represents a ini tag name mapper.
type
NameMapper
func
(
string
)
string
// Built-in name getters.
var
(
// SnackCase converts to format SNACK_CASE.
SnackCase
NameMapper
=
func
(
raw
string
)
string
{
newstr
:=
make
([]
rune
,
0
,
len
(
raw
))
for
i
,
chr
:=
range
raw
{
if
isUpper
:=
'A'
<=
chr
&&
chr
<=
'Z'
;
isUpper
{
if
i
>
0
{
newstr
=
append
(
newstr
,
'_'
)
}
}
newstr
=
append
(
newstr
,
unicode
.
ToUpper
(
chr
))
}
return
string
(
newstr
)
}
// TitleUnderscore converts to format title_underscore.
TitleUnderscore
NameMapper
=
func
(
raw
string
)
string
{
newstr
:=
make
([]
rune
,
0
,
len
(
raw
))
for
i
,
chr
:=
range
raw
{
if
isUpper
:=
'A'
<=
chr
&&
chr
<=
'Z'
;
isUpper
{
if
i
>
0
{
newstr
=
append
(
newstr
,
'_'
)
}
chr
-=
'A'
-
'a'
}
newstr
=
append
(
newstr
,
chr
)
}
return
string
(
newstr
)
}
)
func
(
s
*
Section
)
parseFieldName
(
raw
,
actual
string
)
string
{
if
len
(
actual
)
>
0
{
return
actual
}
if
s
.
f
.
NameMapper
!=
nil
{
return
s
.
f
.
NameMapper
(
raw
)
}
return
raw
}
func
parseDelim
(
actual
string
)
string
{
if
len
(
actual
)
>
0
{
return
actual
}
return
","
}
var
reflectTime
=
reflect
.
TypeOf
(
time
.
Now
())
.
Kind
()
// setSliceWithProperType sets proper values to slice based on its type.
func
setSliceWithProperType
(
key
*
Key
,
field
reflect
.
Value
,
delim
string
,
allowShadow
,
isStrict
bool
)
error
{
var
strs
[]
string
if
allowShadow
{
strs
=
key
.
StringsWithShadows
(
delim
)
}
else
{
strs
=
key
.
Strings
(
delim
)
}
numVals
:=
len
(
strs
)
if
numVals
==
0
{
return
nil
}
var
vals
interface
{}
var
err
error
sliceOf
:=
field
.
Type
()
.
Elem
()
.
Kind
()
switch
sliceOf
{
case
reflect
.
String
:
vals
=
strs
case
reflect
.
Int
:
vals
,
err
=
key
.
parseInts
(
strs
,
true
,
false
)
case
reflect
.
Int64
:
vals
,
err
=
key
.
parseInt64s
(
strs
,
true
,
false
)
case
reflect
.
Uint
:
vals
,
err
=
key
.
parseUints
(
strs
,
true
,
false
)
case
reflect
.
Uint64
:
vals
,
err
=
key
.
parseUint64s
(
strs
,
true
,
false
)
case
reflect
.
Float64
:
vals
,
err
=
key
.
parseFloat64s
(
strs
,
true
,
false
)
case
reflect
.
Bool
:
vals
,
err
=
key
.
parseBools
(
strs
,
true
,
false
)
case
reflectTime
:
vals
,
err
=
key
.
parseTimesFormat
(
time
.
RFC3339
,
strs
,
true
,
false
)
default
:
return
fmt
.
Errorf
(
"unsupported type '[]%s'"
,
sliceOf
)
}
if
err
!=
nil
&&
isStrict
{
return
err
}
slice
:=
reflect
.
MakeSlice
(
field
.
Type
(),
numVals
,
numVals
)
for
i
:=
0
;
i
<
numVals
;
i
++
{
switch
sliceOf
{
case
reflect
.
String
:
slice
.
Index
(
i
)
.
Set
(
reflect
.
ValueOf
(
vals
.
([]
string
)[
i
]))
case
reflect
.
Int
:
slice
.
Index
(
i
)
.
Set
(
reflect
.
ValueOf
(
vals
.
([]
int
)[
i
]))
case
reflect
.
Int64
:
slice
.
Index
(
i
)
.
Set
(
reflect
.
ValueOf
(
vals
.
([]
int64
)[
i
]))
case
reflect
.
Uint
:
slice
.
Index
(
i
)
.
Set
(
reflect
.
ValueOf
(
vals
.
([]
uint
)[
i
]))
case
reflect
.
Uint64
:
slice
.
Index
(
i
)
.
Set
(
reflect
.
ValueOf
(
vals
.
([]
uint64
)[
i
]))
case
reflect
.
Float64
:
slice
.
Index
(
i
)
.
Set
(
reflect
.
ValueOf
(
vals
.
([]
float64
)[
i
]))
case
reflect
.
Bool
:
slice
.
Index
(
i
)
.
Set
(
reflect
.
ValueOf
(
vals
.
([]
bool
)[
i
]))
case
reflectTime
:
slice
.
Index
(
i
)
.
Set
(
reflect
.
ValueOf
(
vals
.
([]
time
.
Time
)[
i
]))
}
}
field
.
Set
(
slice
)
return
nil
}
func
wrapStrictError
(
err
error
,
isStrict
bool
)
error
{
if
isStrict
{
return
err
}
return
nil
}
// setWithProperType sets proper value to field based on its type,
// but it does not return error for failing parsing,
// because we want to use default value that is already assigned to struct.
func
setWithProperType
(
t
reflect
.
Type
,
key
*
Key
,
field
reflect
.
Value
,
delim
string
,
allowShadow
,
isStrict
bool
)
error
{
vt
:=
t
isPtr
:=
t
.
Kind
()
==
reflect
.
Ptr
if
isPtr
{
vt
=
t
.
Elem
()
}
switch
vt
.
Kind
()
{
case
reflect
.
String
:
stringVal
:=
key
.
String
()
if
isPtr
{
field
.
Set
(
reflect
.
ValueOf
(
&
stringVal
))
}
else
if
len
(
stringVal
)
>
0
{
field
.
SetString
(
key
.
String
())
}
case
reflect
.
Bool
:
boolVal
,
err
:=
key
.
Bool
()
if
err
!=
nil
{
return
wrapStrictError
(
err
,
isStrict
)
}
if
isPtr
{
field
.
Set
(
reflect
.
ValueOf
(
&
boolVal
))
}
else
{
field
.
SetBool
(
boolVal
)
}
case
reflect
.
Int
,
reflect
.
Int8
,
reflect
.
Int16
,
reflect
.
Int32
,
reflect
.
Int64
:
// ParseDuration will not return err for `0`, so check the type name
if
vt
.
Name
()
==
"Duration"
{
durationVal
,
err
:=
key
.
Duration
()
if
err
!=
nil
{
if
intVal
,
err
:=
key
.
Int64
();
err
==
nil
{
field
.
SetInt
(
intVal
)
return
nil
}
return
wrapStrictError
(
err
,
isStrict
)
}
if
isPtr
{
field
.
Set
(
reflect
.
ValueOf
(
&
durationVal
))
}
else
if
int64
(
durationVal
)
>
0
{
field
.
Set
(
reflect
.
ValueOf
(
durationVal
))
}
return
nil
}
intVal
,
err
:=
key
.
Int64
()
if
err
!=
nil
{
return
wrapStrictError
(
err
,
isStrict
)
}
if
isPtr
{
pv
:=
reflect
.
New
(
t
.
Elem
())
pv
.
Elem
()
.
SetInt
(
intVal
)
field
.
Set
(
pv
)
}
else
{
field
.
SetInt
(
intVal
)
}
// byte is an alias for uint8, so supporting uint8 breaks support for byte
case
reflect
.
Uint
,
reflect
.
Uint16
,
reflect
.
Uint32
,
reflect
.
Uint64
:
durationVal
,
err
:=
key
.
Duration
()
// Skip zero value
if
err
==
nil
&&
uint64
(
durationVal
)
>
0
{
if
isPtr
{
field
.
Set
(
reflect
.
ValueOf
(
&
durationVal
))
}
else
{
field
.
Set
(
reflect
.
ValueOf
(
durationVal
))
}
return
nil
}
uintVal
,
err
:=
key
.
Uint64
()
if
err
!=
nil
{
return
wrapStrictError
(
err
,
isStrict
)
}
if
isPtr
{
pv
:=
reflect
.
New
(
t
.
Elem
())
pv
.
Elem
()
.
SetUint
(
uintVal
)
field
.
Set
(
pv
)
}
else
{
field
.
SetUint
(
uintVal
)
}
case
reflect
.
Float32
,
reflect
.
Float64
:
floatVal
,
err
:=
key
.
Float64
()
if
err
!=
nil
{
return
wrapStrictError
(
err
,
isStrict
)
}
if
isPtr
{
pv
:=
reflect
.
New
(
t
.
Elem
())
pv
.
Elem
()
.
SetFloat
(
floatVal
)
field
.
Set
(
pv
)
}
else
{
field
.
SetFloat
(
floatVal
)
}
case
reflectTime
:
timeVal
,
err
:=
key
.
Time
()
if
err
!=
nil
{
return
wrapStrictError
(
err
,
isStrict
)
}
if
isPtr
{
field
.
Set
(
reflect
.
ValueOf
(
&
timeVal
))
}
else
{
field
.
Set
(
reflect
.
ValueOf
(
timeVal
))
}
case
reflect
.
Slice
:
return
setSliceWithProperType
(
key
,
field
,
delim
,
allowShadow
,
isStrict
)
default
:
return
fmt
.
Errorf
(
"unsupported type %q"
,
t
)
}
return
nil
}
func
parseTagOptions
(
tag
string
)
(
rawName
string
,
omitEmpty
bool
,
allowShadow
bool
,
allowNonUnique
bool
,
extends
bool
)
{
opts
:=
strings
.
SplitN
(
tag
,
","
,
5
)
rawName
=
opts
[
0
]
for
_
,
opt
:=
range
opts
[
1
:
]
{
omitEmpty
=
omitEmpty
||
(
opt
==
"omitempty"
)
allowShadow
=
allowShadow
||
(
opt
==
"allowshadow"
)
allowNonUnique
=
allowNonUnique
||
(
opt
==
"nonunique"
)
extends
=
extends
||
(
opt
==
"extends"
)
}
return
rawName
,
omitEmpty
,
allowShadow
,
allowNonUnique
,
extends
}
// mapToField maps the given value to the matching field of the given section.
// The sectionIndex is the index (if non unique sections are enabled) to which the value should be added.
func
(
s
*
Section
)
mapToField
(
val
reflect
.
Value
,
isStrict
bool
,
sectionIndex
int
,
sectionName
string
)
error
{
if
val
.
Kind
()
==
reflect
.
Ptr
{
val
=
val
.
Elem
()
}
typ
:=
val
.
Type
()
for
i
:=
0
;
i
<
typ
.
NumField
();
i
++
{
field
:=
val
.
Field
(
i
)
tpField
:=
typ
.
Field
(
i
)
tag
:=
tpField
.
Tag
.
Get
(
"ini"
)
if
tag
==
"-"
{
continue
}
rawName
,
_
,
allowShadow
,
allowNonUnique
,
extends
:=
parseTagOptions
(
tag
)
fieldName
:=
s
.
parseFieldName
(
tpField
.
Name
,
rawName
)
if
len
(
fieldName
)
==
0
||
!
field
.
CanSet
()
{
continue
}
isStruct
:=
tpField
.
Type
.
Kind
()
==
reflect
.
Struct
isStructPtr
:=
tpField
.
Type
.
Kind
()
==
reflect
.
Ptr
&&
tpField
.
Type
.
Elem
()
.
Kind
()
==
reflect
.
Struct
isAnonymousPtr
:=
tpField
.
Type
.
Kind
()
==
reflect
.
Ptr
&&
tpField
.
Anonymous
if
isAnonymousPtr
{
field
.
Set
(
reflect
.
New
(
tpField
.
Type
.
Elem
()))
}
if
extends
&&
(
isAnonymousPtr
||
(
isStruct
&&
tpField
.
Anonymous
))
{
if
isStructPtr
&&
field
.
IsNil
()
{
field
.
Set
(
reflect
.
New
(
tpField
.
Type
.
Elem
()))
}
fieldSection
:=
s
if
rawName
!=
""
{
sectionName
=
s
.
name
+
s
.
f
.
options
.
ChildSectionDelimiter
+
rawName
if
secs
,
err
:=
s
.
f
.
SectionsByName
(
sectionName
);
err
==
nil
&&
sectionIndex
<
len
(
secs
)
{
fieldSection
=
secs
[
sectionIndex
]
}
}
if
err
:=
fieldSection
.
mapToField
(
field
,
isStrict
,
sectionIndex
,
sectionName
);
err
!=
nil
{
return
fmt
.
Errorf
(
"map to field %q: %v"
,
fieldName
,
err
)
}
}
else
if
isAnonymousPtr
||
isStruct
||
isStructPtr
{
if
secs
,
err
:=
s
.
f
.
SectionsByName
(
fieldName
);
err
==
nil
{
if
len
(
secs
)
<=
sectionIndex
{
return
fmt
.
Errorf
(
"there are not enough sections (%d <= %d) for the field %q"
,
len
(
secs
),
sectionIndex
,
fieldName
)
}
// Only set the field to non-nil struct value if we have a section for it.
// Otherwise, we end up with a non-nil struct ptr even though there is no data.
if
isStructPtr
&&
field
.
IsNil
()
{
field
.
Set
(
reflect
.
New
(
tpField
.
Type
.
Elem
()))
}
if
err
=
secs
[
sectionIndex
]
.
mapToField
(
field
,
isStrict
,
sectionIndex
,
fieldName
);
err
!=
nil
{
return
fmt
.
Errorf
(
"map to field %q: %v"
,
fieldName
,
err
)
}
continue
}
}
// Map non-unique sections
if
allowNonUnique
&&
tpField
.
Type
.
Kind
()
==
reflect
.
Slice
{
newField
,
err
:=
s
.
mapToSlice
(
fieldName
,
field
,
isStrict
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"map to slice %q: %v"
,
fieldName
,
err
)
}
field
.
Set
(
newField
)
continue
}
if
key
,
err
:=
s
.
GetKey
(
fieldName
);
err
==
nil
{
delim
:=
parseDelim
(
tpField
.
Tag
.
Get
(
"delim"
))
if
err
=
setWithProperType
(
tpField
.
Type
,
key
,
field
,
delim
,
allowShadow
,
isStrict
);
err
!=
nil
{
return
fmt
.
Errorf
(
"set field %q: %v"
,
fieldName
,
err
)
}
}
}
return
nil
}
// mapToSlice maps all sections with the same name and returns the new value.
// The type of the Value must be a slice.
func
(
s
*
Section
)
mapToSlice
(
secName
string
,
val
reflect
.
Value
,
isStrict
bool
)
(
reflect
.
Value
,
error
)
{
secs
,
err
:=
s
.
f
.
SectionsByName
(
secName
)
if
err
!=
nil
{
return
reflect
.
Value
{},
err
}
typ
:=
val
.
Type
()
.
Elem
()
for
i
,
sec
:=
range
secs
{
elem
:=
reflect
.
New
(
typ
)
if
err
=
sec
.
mapToField
(
elem
,
isStrict
,
i
,
sec
.
name
);
err
!=
nil
{
return
reflect
.
Value
{},
fmt
.
Errorf
(
"map to field from section %q: %v"
,
secName
,
err
)
}
val
=
reflect
.
Append
(
val
,
elem
.
Elem
())
}
return
val
,
nil
}
// mapTo maps a section to object v.
func
(
s
*
Section
)
mapTo
(
v
interface
{},
isStrict
bool
)
error
{
typ
:=
reflect
.
TypeOf
(
v
)
val
:=
reflect
.
ValueOf
(
v
)
if
typ
.
Kind
()
==
reflect
.
Ptr
{
typ
=
typ
.
Elem
()
val
=
val
.
Elem
()
}
else
{
return
errors
.
New
(
"not a pointer to a struct"
)
}
if
typ
.
Kind
()
==
reflect
.
Slice
{
newField
,
err
:=
s
.
mapToSlice
(
s
.
name
,
val
,
isStrict
)
if
err
!=
nil
{
return
err
}
val
.
Set
(
newField
)
return
nil
}
return
s
.
mapToField
(
val
,
isStrict
,
0
,
s
.
name
)
}
// MapTo maps section to given struct.
func
(
s
*
Section
)
MapTo
(
v
interface
{})
error
{
return
s
.
mapTo
(
v
,
false
)
}
// StrictMapTo maps section to given struct in strict mode,
// which returns all possible error including value parsing error.
func
(
s
*
Section
)
StrictMapTo
(
v
interface
{})
error
{
return
s
.
mapTo
(
v
,
true
)
}
// MapTo maps file to given struct.
func
(
f
*
File
)
MapTo
(
v
interface
{})
error
{
return
f
.
Section
(
""
)
.
MapTo
(
v
)
}
// StrictMapTo maps file to given struct in strict mode,
// which returns all possible error including value parsing error.
func
(
f
*
File
)
StrictMapTo
(
v
interface
{})
error
{
return
f
.
Section
(
""
)
.
StrictMapTo
(
v
)
}
// MapToWithMapper maps data sources to given struct with name mapper.
func
MapToWithMapper
(
v
interface
{},
mapper
NameMapper
,
source
interface
{},
others
...
interface
{})
error
{
cfg
,
err
:=
Load
(
source
,
others
...
)
if
err
!=
nil
{
return
err
}
cfg
.
NameMapper
=
mapper
return
cfg
.
MapTo
(
v
)
}
// StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode,
// which returns all possible error including value parsing error.
func
StrictMapToWithMapper
(
v
interface
{},
mapper
NameMapper
,
source
interface
{},
others
...
interface
{})
error
{
cfg
,
err
:=
Load
(
source
,
others
...
)
if
err
!=
nil
{
return
err
}
cfg
.
NameMapper
=
mapper
return
cfg
.
StrictMapTo
(
v
)
}
// MapTo maps data sources to given struct.
func
MapTo
(
v
,
source
interface
{},
others
...
interface
{})
error
{
return
MapToWithMapper
(
v
,
nil
,
source
,
others
...
)
}
// StrictMapTo maps data sources to given struct in strict mode,
// which returns all possible error including value parsing error.
func
StrictMapTo
(
v
,
source
interface
{},
others
...
interface
{})
error
{
return
StrictMapToWithMapper
(
v
,
nil
,
source
,
others
...
)
}
// reflectSliceWithProperType does the opposite thing as setSliceWithProperType.
func
reflectSliceWithProperType
(
key
*
Key
,
field
reflect
.
Value
,
delim
string
,
allowShadow
bool
)
error
{
slice
:=
field
.
Slice
(
0
,
field
.
Len
())
if
field
.
Len
()
==
0
{
return
nil
}
sliceOf
:=
field
.
Type
()
.
Elem
()
.
Kind
()
if
allowShadow
{
var
keyWithShadows
*
Key
for
i
:=
0
;
i
<
field
.
Len
();
i
++
{
var
val
string
switch
sliceOf
{
case
reflect
.
String
:
val
=
slice
.
Index
(
i
)
.
String
()
case
reflect
.
Int
,
reflect
.
Int64
:
val
=
fmt
.
Sprint
(
slice
.
Index
(
i
)
.
Int
())
case
reflect
.
Uint
,
reflect
.
Uint64
:
val
=
fmt
.
Sprint
(
slice
.
Index
(
i
)
.
Uint
())
case
reflect
.
Float64
:
val
=
fmt
.
Sprint
(
slice
.
Index
(
i
)
.
Float
())
case
reflect
.
Bool
:
val
=
fmt
.
Sprint
(
slice
.
Index
(
i
)
.
Bool
())
case
reflectTime
:
val
=
slice
.
Index
(
i
)
.
Interface
()
.
(
time
.
Time
)
.
Format
(
time
.
RFC3339
)
default
:
return
fmt
.
Errorf
(
"unsupported type '[]%s'"
,
sliceOf
)
}
if
i
==
0
{
keyWithShadows
=
newKey
(
key
.
s
,
key
.
name
,
val
)
}
else
{
_
=
keyWithShadows
.
AddShadow
(
val
)
}
}
*
key
=
*
keyWithShadows
return
nil
}
var
buf
bytes
.
Buffer
for
i
:=
0
;
i
<
field
.
Len
();
i
++
{
switch
sliceOf
{
case
reflect
.
String
:
buf
.
WriteString
(
slice
.
Index
(
i
)
.
String
())
case
reflect
.
Int
,
reflect
.
Int64
:
buf
.
WriteString
(
fmt
.
Sprint
(
slice
.
Index
(
i
)
.
Int
()))
case
reflect
.
Uint
,
reflect
.
Uint64
:
buf
.
WriteString
(
fmt
.
Sprint
(
slice
.
Index
(
i
)
.
Uint
()))
case
reflect
.
Float64
:
buf
.
WriteString
(
fmt
.
Sprint
(
slice
.
Index
(
i
)
.
Float
()))
case
reflect
.
Bool
:
buf
.
WriteString
(
fmt
.
Sprint
(
slice
.
Index
(
i
)
.
Bool
()))
case
reflectTime
:
buf
.
WriteString
(
slice
.
Index
(
i
)
.
Interface
()
.
(
time
.
Time
)
.
Format
(
time
.
RFC3339
))
default
:
return
fmt
.
Errorf
(
"unsupported type '[]%s'"
,
sliceOf
)
}
buf
.
WriteString
(
delim
)
}
key
.
SetValue
(
buf
.
String
()[
:
buf
.
Len
()
-
len
(
delim
)])
return
nil
}
// reflectWithProperType does the opposite thing as setWithProperType.
func
reflectWithProperType
(
t
reflect
.
Type
,
key
*
Key
,
field
reflect
.
Value
,
delim
string
,
allowShadow
bool
)
error
{
switch
t
.
Kind
()
{
case
reflect
.
String
:
key
.
SetValue
(
field
.
String
())
case
reflect
.
Bool
:
key
.
SetValue
(
fmt
.
Sprint
(
field
.
Bool
()))
case
reflect
.
Int
,
reflect
.
Int8
,
reflect
.
Int16
,
reflect
.
Int32
,
reflect
.
Int64
:
key
.
SetValue
(
fmt
.
Sprint
(
field
.
Int
()))
case
reflect
.
Uint
,
reflect
.
Uint8
,
reflect
.
Uint16
,
reflect
.
Uint32
,
reflect
.
Uint64
:
key
.
SetValue
(
fmt
.
Sprint
(
field
.
Uint
()))
case
reflect
.
Float32
,
reflect
.
Float64
:
key
.
SetValue
(
fmt
.
Sprint
(
field
.
Float
()))
case
reflectTime
:
key
.
SetValue
(
fmt
.
Sprint
(
field
.
Interface
()
.
(
time
.
Time
)
.
Format
(
time
.
RFC3339
)))
case
reflect
.
Slice
:
return
reflectSliceWithProperType
(
key
,
field
,
delim
,
allowShadow
)
case
reflect
.
Ptr
:
if
!
field
.
IsNil
()
{
return
reflectWithProperType
(
t
.
Elem
(),
key
,
field
.
Elem
(),
delim
,
allowShadow
)
}
default
:
return
fmt
.
Errorf
(
"unsupported type %q"
,
t
)
}
return
nil
}
// CR: copied from encoding/json/encode.go with modifications of time.Time support.
// TODO: add more test coverage.
func
isEmptyValue
(
v
reflect
.
Value
)
bool
{
switch
v
.
Kind
()
{
case
reflect
.
Array
,
reflect
.
Map
,
reflect
.
Slice
,
reflect
.
String
:
return
v
.
Len
()
==
0
case
reflect
.
Bool
:
return
!
v
.
Bool
()
case
reflect
.
Int
,
reflect
.
Int8
,
reflect
.
Int16
,
reflect
.
Int32
,
reflect
.
Int64
:
return
v
.
Int
()
==
0
case
reflect
.
Uint
,
reflect
.
Uint8
,
reflect
.
Uint16
,
reflect
.
Uint32
,
reflect
.
Uint64
,
reflect
.
Uintptr
:
return
v
.
Uint
()
==
0
case
reflect
.
Float32
,
reflect
.
Float64
:
return
v
.
Float
()
==
0
case
reflect
.
Interface
,
reflect
.
Ptr
:
return
v
.
IsNil
()
case
reflectTime
:
t
,
ok
:=
v
.
Interface
()
.
(
time
.
Time
)
return
ok
&&
t
.
IsZero
()
}
return
false
}
// StructReflector is the interface implemented by struct types that can extract themselves into INI objects.
type
StructReflector
interface
{
ReflectINIStruct
(
*
File
)
error
}
func
(
s
*
Section
)
reflectFrom
(
val
reflect
.
Value
)
error
{
if
val
.
Kind
()
==
reflect
.
Ptr
{
val
=
val
.
Elem
()
}
typ
:=
val
.
Type
()
for
i
:=
0
;
i
<
typ
.
NumField
();
i
++
{
if
!
val
.
Field
(
i
)
.
CanInterface
()
{
continue
}
field
:=
val
.
Field
(
i
)
tpField
:=
typ
.
Field
(
i
)
tag
:=
tpField
.
Tag
.
Get
(
"ini"
)
if
tag
==
"-"
{
continue
}
rawName
,
omitEmpty
,
allowShadow
,
allowNonUnique
,
extends
:=
parseTagOptions
(
tag
)
if
omitEmpty
&&
isEmptyValue
(
field
)
{
continue
}
if
r
,
ok
:=
field
.
Interface
()
.
(
StructReflector
);
ok
{
return
r
.
ReflectINIStruct
(
s
.
f
)
}
fieldName
:=
s
.
parseFieldName
(
tpField
.
Name
,
rawName
)
if
len
(
fieldName
)
==
0
||
!
field
.
CanSet
()
{
continue
}
if
extends
&&
tpField
.
Anonymous
&&
(
tpField
.
Type
.
Kind
()
==
reflect
.
Ptr
||
tpField
.
Type
.
Kind
()
==
reflect
.
Struct
)
{
if
err
:=
s
.
reflectFrom
(
field
);
err
!=
nil
{
return
fmt
.
Errorf
(
"reflect from field %q: %v"
,
fieldName
,
err
)
}
continue
}
if
(
tpField
.
Type
.
Kind
()
==
reflect
.
Ptr
&&
tpField
.
Type
.
Elem
()
.
Kind
()
==
reflect
.
Struct
)
||
(
tpField
.
Type
.
Kind
()
==
reflect
.
Struct
&&
tpField
.
Type
.
Name
()
!=
"Time"
)
{
// Note: The only error here is section doesn't exist.
sec
,
err
:=
s
.
f
.
GetSection
(
fieldName
)
if
err
!=
nil
{
// Note: fieldName can never be empty here, ignore error.
sec
,
_
=
s
.
f
.
NewSection
(
fieldName
)
}
// Add comment from comment tag
if
len
(
sec
.
Comment
)
==
0
{
sec
.
Comment
=
tpField
.
Tag
.
Get
(
"comment"
)
}
if
err
=
sec
.
reflectFrom
(
field
);
err
!=
nil
{
return
fmt
.
Errorf
(
"reflect from field %q: %v"
,
fieldName
,
err
)
}
continue
}
if
allowNonUnique
&&
tpField
.
Type
.
Kind
()
==
reflect
.
Slice
{
slice
:=
field
.
Slice
(
0
,
field
.
Len
())
if
field
.
Len
()
==
0
{
return
nil
}
sliceOf
:=
field
.
Type
()
.
Elem
()
.
Kind
()
for
i
:=
0
;
i
<
field
.
Len
();
i
++
{
if
sliceOf
!=
reflect
.
Struct
&&
sliceOf
!=
reflect
.
Ptr
{
return
fmt
.
Errorf
(
"field %q is not a slice of pointer or struct"
,
fieldName
)
}
sec
,
err
:=
s
.
f
.
NewSection
(
fieldName
)
if
err
!=
nil
{
return
err
}
// Add comment from comment tag
if
len
(
sec
.
Comment
)
==
0
{
sec
.
Comment
=
tpField
.
Tag
.
Get
(
"comment"
)
}
if
err
:=
sec
.
reflectFrom
(
slice
.
Index
(
i
));
err
!=
nil
{
return
fmt
.
Errorf
(
"reflect from field %q: %v"
,
fieldName
,
err
)
}
}
continue
}
// Note: Same reason as section.
key
,
err
:=
s
.
GetKey
(
fieldName
)
if
err
!=
nil
{
key
,
_
=
s
.
NewKey
(
fieldName
,
""
)
}
// Add comment from comment tag
if
len
(
key
.
Comment
)
==
0
{
key
.
Comment
=
tpField
.
Tag
.
Get
(
"comment"
)
}
delim
:=
parseDelim
(
tpField
.
Tag
.
Get
(
"delim"
))
if
err
=
reflectWithProperType
(
tpField
.
Type
,
key
,
field
,
delim
,
allowShadow
);
err
!=
nil
{
return
fmt
.
Errorf
(
"reflect field %q: %v"
,
fieldName
,
err
)
}
}
return
nil
}
// ReflectFrom reflects section from given struct. It overwrites existing ones.
func
(
s
*
Section
)
ReflectFrom
(
v
interface
{})
error
{
typ
:=
reflect
.
TypeOf
(
v
)
val
:=
reflect
.
ValueOf
(
v
)
if
s
.
name
!=
DefaultSection
&&
s
.
f
.
options
.
AllowNonUniqueSections
&&
(
typ
.
Kind
()
==
reflect
.
Slice
||
typ
.
Kind
()
==
reflect
.
Ptr
)
{
// Clear sections to make sure none exists before adding the new ones
s
.
f
.
DeleteSection
(
s
.
name
)
if
typ
.
Kind
()
==
reflect
.
Ptr
{
sec
,
err
:=
s
.
f
.
NewSection
(
s
.
name
)
if
err
!=
nil
{
return
err
}
return
sec
.
reflectFrom
(
val
.
Elem
())
}
slice
:=
val
.
Slice
(
0
,
val
.
Len
())
sliceOf
:=
val
.
Type
()
.
Elem
()
.
Kind
()
if
sliceOf
!=
reflect
.
Ptr
{
return
fmt
.
Errorf
(
"not a slice of pointers"
)
}
for
i
:=
0
;
i
<
slice
.
Len
();
i
++
{
sec
,
err
:=
s
.
f
.
NewSection
(
s
.
name
)
if
err
!=
nil
{
return
err
}
err
=
sec
.
reflectFrom
(
slice
.
Index
(
i
))
if
err
!=
nil
{
return
fmt
.
Errorf
(
"reflect from %dth field: %v"
,
i
,
err
)
}
}
return
nil
}
if
typ
.
Kind
()
==
reflect
.
Ptr
{
val
=
val
.
Elem
()
}
else
{
return
errors
.
New
(
"not a pointer to a struct"
)
}
return
s
.
reflectFrom
(
val
)
}
// ReflectFrom reflects file from given struct.
func
(
f
*
File
)
ReflectFrom
(
v
interface
{})
error
{
return
f
.
Section
(
""
)
.
ReflectFrom
(
v
)
}
// ReflectFromWithMapper reflects data sources from given struct with name mapper.
func
ReflectFromWithMapper
(
cfg
*
File
,
v
interface
{},
mapper
NameMapper
)
error
{
cfg
.
NameMapper
=
mapper
return
cfg
.
ReflectFrom
(
v
)
}
// ReflectFrom reflects data sources from given struct.
func
ReflectFrom
(
cfg
*
File
,
v
interface
{})
error
{
return
ReflectFromWithMapper
(
cfg
,
v
,
nil
)
}
vendor/modules.txt
View file @
ae831b78
...
@@ -38,6 +38,9 @@ golang.org/x/mod/semver
...
@@ -38,6 +38,9 @@ golang.org/x/mod/semver
## explicit; go 1.18
## explicit; go 1.18
golang.org/x/sys/unix
golang.org/x/sys/unix
golang.org/x/sys/windows
golang.org/x/sys/windows
# gopkg.in/ini.v1 v1.67.0
## explicit
gopkg.in/ini.v1
# sigs.k8s.io/yaml v1.4.0
# sigs.k8s.io/yaml v1.4.0
## explicit; go 1.12
## explicit; go 1.12
sigs.k8s.io/yaml
sigs.k8s.io/yaml
...
...
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment