Compare commits

..

No commits in common. "master" and "1.0.1" have entirely different histories.

29 changed files with 542 additions and 1101 deletions

View File

@ -1,74 +1,89 @@
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: build-linux name: cleanup-before
steps: steps:
- name: build-linux-amd64 - name: clean
image: golang image: alpine
commands: commands:
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES - rm -rf /build/*
environment: volumes:
GOOS: linux - name: build
GOARCH: amd64 path: /build
GOOPTIONS: -mod=vendor
SRCFILES: cmd/weather/*.go
PROJECTNAME: weather
when: when:
event: event: tag
exclude:
- tag volumes:
- name: build-linux-arm64 - name: build
image: golang host:
commands: path: /tmp/weather/build
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES
environment:
GOOS: linux
GOARCH: arm64
GOOPTIONS: -mod=vendor
SRCFILES: cmd/weather/*.go
PROJECTNAME: weather
when:
event:
exclude:
- tag
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: gitea-release-linux name: default-linux-amd64
steps: steps:
- name: build-linux-amd64 - name: build
image: golang image: golang
commands: commands:
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES - ./ci-build.sh build
- tar -czvf $PROJECTNAME-$DRONE_TAG-$GOOS-$GOARCH.tar.gz $PROJECTNAME
- echo $PROJECTNAME $DRONE_TAG > VERSION
environment: environment:
GOOS: linux GOOS: linux
GOARCH: amd64 GOARCH: amd64
GOOPTIONS: -mod=vendor volumes:
SRCFILES: cmd/weather/*.go - name: build
PROJECTNAME: weather path: /build
when:
event: volumes:
- tag - name: build
- name: build-linux-arm64 host:
path: /tmp/weather/build
depends_on:
- cleanup-before
---
kind: pipeline
type: docker
name: default-linux-arm64
steps:
- name: build
image: golang image: golang
commands: commands:
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES - ./ci-build.sh build
- tar -czvf $PROJECTNAME-$DRONE_TAG-$GOOS-$GOARCH.tar.gz $PROJECTNAME
- echo $PROJECTNAME $DRONE_TAG > VERSION
environment: environment:
GOOS: linux GOOS: linux
GOARCH: arm64 GOARCH: arm64
GOOPTIONS: -mod=vendor volumes:
SRCFILES: cmd/weather/*.go - name: build
PROJECTNAME: weather path: /build
volumes:
- name: build
host:
path: /tmp/weather/build
depends_on:
- cleanup-before
---
kind: pipeline
type: docker
name: gitea-release
steps:
- name: move
image: alpine
commands:
- mv build/* ./
volumes:
- name: build
path: /drone/src/build
when: when:
event: event: tag
- tag
- name: release - name: release
image: plugins/gitea-release image: plugins/gitea-release
settings: settings:
@ -80,6 +95,50 @@ steps:
- sha256 - sha256
- sha512 - sha512
title: VERSION title: VERSION
volumes:
- name: build
path: /drone/src/build
when: when:
event: event: tag
- tag - name: ls
image: alpine
commands:
- find .
volumes:
- name: build
path: /drone/src/build
when:
event: tag
volumes:
- name: build
host:
path: /tmp/weather/build
depends_on:
- default-linux-amd64
- default-linux-arm64
---
kind: pipeline
type: docker
name: cleanup-after
steps:
- name: clean
image: alpine
commands:
- rm -rf /build/*
volumes:
- name: build
path: /build
when:
event: tag
volumes:
- name: build
host:
path: /tmp/weather/build
depends_on:
- gitea-release

17
Makefile Normal file
View File

@ -0,0 +1,17 @@
# weather Makefile
PROJECT=weather
GOCMD=go
GOBUILDCMD=${GOCMD} build
GOOPTIONS=-mod=vendor -ldflags="-s -w" -o ${PROJECT}
RMCMD=/bin/rm
SRCFILES=cmd/weather/*.go
all: build
build:
${GOBUILDCMD} ${GOOPTIONS} ${SRCFILES}
clean:
${RMCMD} ${PROJECT}

View File

@ -1,5 +1,4 @@
# weather # weather
[![Build Status](https://drone.paulbsd.com/api/badges/paulbsd/weather/status.svg)](https://drone.paulbsd.com/paulbsd/weather)
## Summary ## Summary
@ -40,7 +39,7 @@ units=metric
## License ## License
```text ```text
Copyright (c) 2020, 2021 PaulBSD Copyright (c) 2020, PaulBSD
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

60
ci-build.sh Executable file
View File

@ -0,0 +1,60 @@
#!/bin/bash
PROJECTNAME=weather
RELEASENAME=${PROJECTNAME}
VERSION="0"
GOOPTIONS="-mod=vendor"
SRCFILES=cmd/weather/*.go
build() {
echo "Begin of build"
if [[ ! -z $DRONE_TAG ]]
then
echo "Drone tag set, let's do a release"
VERSION=$DRONE_TAG
echo "${PROJECTNAME} ${VERSION}" > /build/VERSION
elif [[ ! -z $DRONE_TAG ]]
then
echo "Drone not set, let's only do a build"
VERSION=$DRONE_COMMIT
fi
if [[ ! -z $VERSION && ! -z $GOOS && ! -z $GOARCH ]]
then
echo "Let's set a release name"
RELEASENAME=${PROJECTNAME}-${VERSION}-${GOOS}-${GOARCH}
fi
echo "Building project"
go build -o ${PROJECTNAME} ${GOOPTIONS} ${SRCFILES}
if [[ ! -z $DRONE_TAG ]]
then
echo "Let's make archives"
mkdir -p /build
tar -czvf /build/${RELEASENAME}.tar.gz ${PROJECTNAME}
fi
echo "Removing binary file"
rm ${PROJECTNAME}
echo "End of build"
}
clean() {
rm -rf $RELEASEDIR
}
case $1 in
"build")
build
;;
"clean")
clean
;;
*)
echo "No options choosen"
exit 1
;;
esac

12
go.mod
View File

@ -1,13 +1,9 @@
module git.paulbsd.com/paulbsd/weather module git.paulbsd.com/paulbsd/weather
go 1.21 go 1.15
require ( require (
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc
gopkg.in/ini.v1 v1.67.0 github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect
) gopkg.in/ini.v1 v1.44.0
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/stretchr/testify v1.3.0 // indirect
) )

29
go.sum
View File

@ -1,12 +1,17 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc h1:KpMgaYJRieDkHZJWY3LMafvtqS/U8xX6+lUN+OKpl/Y=
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 h1:N8Bg45zpk/UcpNGnfJt2y/3lRWASHNTUET8owPYCgYI=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0=
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

View File

@ -3,7 +3,7 @@ package data
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io/ioutil"
"net/http" "net/http"
"time" "time"
@ -33,7 +33,7 @@ func FetchData(c *config.Config, city string) (Data, error) {
defer r.Body.Close() defer r.Body.Close()
b, err := io.ReadAll(r.Body) b, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
return Data{}, err return Data{}, err
} }
@ -84,12 +84,11 @@ func SendDataToInflux(c *config.Config, d Data) error {
} }
bp.AddPoint(point) bp.AddPoint(point)
}
err = httpClient.Write(bp) err = httpClient.Write(bp)
if err != nil { if err != nil {
return err return err
} }
}
return nil return nil
} }

View File

@ -1,5 +1,5 @@
# influxdb1-clientv2 # influxdb1-clientv2
influxdb1-clientv2 is the current Go client API for InfluxDB 1.x. For connecting to InfluxDB 2.x see the [influxdb-client-go](https://github.com/influxdata/influxdb-client-go) client library. influxdb1-clientv2 is the current Go client API for InfluxDB 1.x. A Go client for the 2.0 API will be coming soon.
InfluxDB is an open-source distributed time series database, find more about [InfluxDB](https://www.influxdata.com/time-series-platform/influxdb/) at https://docs.influxdata.com/influxdb/latest InfluxDB is an open-source distributed time series database, find more about [InfluxDB](https://www.influxdata.com/time-series-platform/influxdb/) at https://docs.influxdata.com/influxdb/latest
@ -9,15 +9,6 @@ To import into your Go project, run the following command in your terminal:
Then, in your import declaration section of your Go file, paste the following: Then, in your import declaration section of your Go file, paste the following:
`import "github.com/influxdata/influxdb1-client/v2"` `import "github.com/influxdata/influxdb1-client/v2"`
If you get the error `build github.com/user/influx: cannot find module for path github.com/influxdata/influxdb1-client/v2` when trying to build:
change your import to:
```go
import(
_ "github.com/influxdata/influxdb1-client" // this is important because of the bug in go mod
client "github.com/influxdata/influxdb1-client/v2"
)
```
## Example ## Example
The following example creates a new client to the InfluxDB host on localhost:8086 and runs a query for the measurement `cpu_load` from the `mydb` database. The following example creates a new client to the InfluxDB host on localhost:8086 and runs a query for the measurement `cpu_load` from the `mydb` database.
``` go ``` go

View File

@ -247,10 +247,7 @@ func (c *Client) QueryContext(ctx context.Context, q Query) (*Response, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer func() { defer resp.Body.Close()
io.Copy(ioutil.Discard, resp.Body) // https://github.com/influxdata/influxdb1-client/issues/58
resp.Body.Close()
}()
var response Response var response Response
if q.Chunked { if q.Chunked {

View File

@ -3,7 +3,6 @@ package client // import "github.com/influxdata/influxdb1-client/v2"
import ( import (
"bytes" "bytes"
"compress/gzip"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"errors" "errors"
@ -21,13 +20,6 @@ import (
"github.com/influxdata/influxdb1-client/models" "github.com/influxdata/influxdb1-client/models"
) )
type ContentEncoding string
const (
DefaultEncoding ContentEncoding = ""
GzipEncoding ContentEncoding = "gzip"
)
// HTTPConfig is the config data needed to create an HTTP Client. // HTTPConfig is the config data needed to create an HTTP Client.
type HTTPConfig struct { type HTTPConfig struct {
// Addr should be of the form "http://host:port" // Addr should be of the form "http://host:port"
@ -56,9 +48,6 @@ type HTTPConfig struct {
// Proxy configures the Proxy function on the HTTP client. // Proxy configures the Proxy function on the HTTP client.
Proxy func(req *http.Request) (*url.URL, error) Proxy func(req *http.Request) (*url.URL, error)
// WriteEncoding specifies the encoding of write request
WriteEncoding ContentEncoding
} }
// BatchPointsConfig is the config data needed to create an instance of the BatchPoints struct. // BatchPointsConfig is the config data needed to create an instance of the BatchPoints struct.
@ -113,12 +102,6 @@ func NewHTTPClient(conf HTTPConfig) (Client, error) {
return nil, errors.New(m) return nil, errors.New(m)
} }
switch conf.WriteEncoding {
case DefaultEncoding, GzipEncoding:
default:
return nil, fmt.Errorf("unsupported encoding %s", conf.WriteEncoding)
}
tr := &http.Transport{ tr := &http.Transport{
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
InsecureSkipVerify: conf.InsecureSkipVerify, InsecureSkipVerify: conf.InsecureSkipVerify,
@ -138,7 +121,6 @@ func NewHTTPClient(conf HTTPConfig) (Client, error) {
Transport: tr, Transport: tr,
}, },
transport: tr, transport: tr,
encoding: conf.WriteEncoding,
}, nil }, nil
} }
@ -204,7 +186,6 @@ type client struct {
useragent string useragent string
httpClient *http.Client httpClient *http.Client
transport *http.Transport transport *http.Transport
encoding ContentEncoding
} }
// BatchPoints is an interface into a batched grouping of points to write into // BatchPoints is an interface into a batched grouping of points to write into
@ -385,29 +366,15 @@ func NewPointFrom(pt models.Point) *Point {
func (c *client) Write(bp BatchPoints) error { func (c *client) Write(bp BatchPoints) error {
var b bytes.Buffer var b bytes.Buffer
var w io.Writer
if c.encoding == GzipEncoding {
w = gzip.NewWriter(&b)
} else {
w = &b
}
for _, p := range bp.Points() { for _, p := range bp.Points() {
if p == nil { if p == nil {
continue continue
} }
if _, err := io.WriteString(w, p.pt.PrecisionString(bp.Precision())); err != nil { if _, err := b.WriteString(p.pt.PrecisionString(bp.Precision())); err != nil {
return err return err
} }
if _, err := w.Write([]byte{'\n'}); err != nil { if err := b.WriteByte('\n'); err != nil {
return err
}
}
// gzip writer should be closed to flush data into underlying buffer
if c, ok := w.(io.Closer); ok {
if err := c.Close(); err != nil {
return err return err
} }
} }
@ -419,9 +386,6 @@ func (c *client) Write(bp BatchPoints) error {
if err != nil { if err != nil {
return err return err
} }
if c.encoding != DefaultEncoding {
req.Header.Set("Content-Encoding", string(c.encoding))
}
req.Header.Set("Content-Type", "") req.Header.Set("Content-Type", "")
req.Header.Set("User-Agent", c.useragent) req.Header.Set("User-Agent", c.useragent)
if c.username != "" { if c.username != "" {
@ -465,9 +429,6 @@ type Query struct {
Parameters map[string]interface{} Parameters map[string]interface{}
} }
// Params is a type alias to the query parameters.
type Params map[string]interface{}
// NewQuery returns a query object. // NewQuery returns a query object.
// The database and precision arguments can be empty strings if they are not needed for the query. // The database and precision arguments can be empty strings if they are not needed for the query.
func NewQuery(command, database, precision string) Query { func NewQuery(command, database, precision string) Query {
@ -532,7 +493,6 @@ type Message struct {
// Result represents a resultset returned from a single statement. // Result represents a resultset returned from a single statement.
type Result struct { type Result struct {
StatementId int `json:"statement_id"`
Series []models.Row Series []models.Row
Messages []*Message Messages []*Message
Err string `json:"error,omitempty"` Err string `json:"error,omitempty"`
@ -556,10 +516,7 @@ func (c *client) Query(q Query) (*Response, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer func() { defer resp.Body.Close()
io.Copy(ioutil.Discard, resp.Body) // https://github.com/influxdata/influxdb1-client/issues/58
resp.Body.Close()
}()
if err := checkResponse(resp); err != nil { if err := checkResponse(resp); err != nil {
return nil, err return nil, err

View File

@ -1,73 +0,0 @@
package client
import (
"encoding/json"
"time"
)
type (
// Identifier is an identifier value.
Identifier string
// StringValue is a string literal.
StringValue string
// RegexValue is a regexp literal.
RegexValue string
// NumberValue is a number literal.
NumberValue float64
// IntegerValue is an integer literal.
IntegerValue int64
// BooleanValue is a boolean literal.
BooleanValue bool
// TimeValue is a time literal.
TimeValue time.Time
// DurationValue is a duration literal.
DurationValue time.Duration
)
func (v Identifier) MarshalJSON() ([]byte, error) {
m := map[string]string{"identifier": string(v)}
return json.Marshal(m)
}
func (v StringValue) MarshalJSON() ([]byte, error) {
m := map[string]string{"string": string(v)}
return json.Marshal(m)
}
func (v RegexValue) MarshalJSON() ([]byte, error) {
m := map[string]string{"regex": string(v)}
return json.Marshal(m)
}
func (v NumberValue) MarshalJSON() ([]byte, error) {
m := map[string]float64{"number": float64(v)}
return json.Marshal(m)
}
func (v IntegerValue) MarshalJSON() ([]byte, error) {
m := map[string]int64{"integer": int64(v)}
return json.Marshal(m)
}
func (v BooleanValue) MarshalJSON() ([]byte, error) {
m := map[string]bool{"boolean": bool(v)}
return json.Marshal(m)
}
func (v TimeValue) MarshalJSON() ([]byte, error) {
t := time.Time(v)
m := map[string]string{"string": t.Format(time.RFC3339Nano)}
return json.Marshal(m)
}
func (v DurationValue) MarshalJSON() ([]byte, error) {
m := map[string]int64{"duration": int64(v)}
return json.Marshal(m)
}

12
vendor/gopkg.in/ini.v1/.editorconfig generated vendored
View File

@ -1,12 +0,0 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*_test.go]
trim_trailing_whitespace = false

1
vendor/gopkg.in/ini.v1/.gitignore generated vendored
View File

@ -4,4 +4,3 @@ ini.sublime-workspace
testdata/conf_reflect.ini testdata/conf_reflect.ini
.idea .idea
/.vscode /.vscode
.DS_Store

27
vendor/gopkg.in/ini.v1/.golangci.yml generated vendored
View File

@ -1,27 +0,0 @@
linters-settings:
staticcheck:
checks: [
"all",
"-SA1019" # There are valid use cases of strings.Title
]
nakedret:
max-func-lines: 0 # Disallow any unnamed return statement
linters:
enable:
- deadcode
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
- nakedret
- gofmt
- rowserrcheck
- unconvert
- goimports
- unparam

18
vendor/gopkg.in/ini.v1/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,18 @@
sudo: false
language: go
go:
- 1.6.x
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
- 1.12.x
script:
- go get golang.org/x/tools/cmd/cover
- go get github.com/smartystreets/goconvey
- mkdir -p $HOME/gopath/src/gopkg.in
- ln -s $HOME/gopath/src/github.com/go-ini/ini $HOME/gopath/src/gopkg.in/ini.v1
- cd $HOME/gopath/src/gopkg.in/ini.v1
- go test -v -cover -race

2
vendor/gopkg.in/ini.v1/Makefile generated vendored
View File

@ -6,7 +6,7 @@ test:
go test -v -cover -race go test -v -cover -race
bench: bench:
go test -v -cover -test.bench=. -test.benchmem go test -v -cover -race -test.bench=. -test.benchmem
vet: vet:
go vet go vet

21
vendor/gopkg.in/ini.v1/README.md generated vendored
View File

@ -1,9 +1,5 @@
# INI INI [![Build Status](https://travis-ci.org/go-ini/ini.svg?branch=master)](https://travis-ci.org/go-ini/ini) [![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg)](https://sourcegraph.com/github.com/go-ini/ini)
===
[![GitHub Workflow Status](https://img.shields.io/github/checks-status/go-ini/ini/main?logo=github&style=for-the-badge)](https://github.com/go-ini/ini/actions?query=branch%3Amain)
[![codecov](https://img.shields.io/codecov/c/github/go-ini/ini/master?logo=codecov&style=for-the-badge)](https://codecov.io/gh/go-ini/ini)
[![GoDoc](https://img.shields.io/badge/GoDoc-Reference-blue?style=for-the-badge&logo=go)](https://pkg.go.dev/github.com/go-ini/ini?tab=doc)
[![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg?style=for-the-badge&logo=sourcegraph)](https://sourcegraph.com/github.com/go-ini/ini)
![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200) ![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200)
@ -11,7 +7,7 @@ Package ini provides INI file read and write functionality in Go.
## Features ## Features
- Load from multiple data sources(file, `[]byte`, `io.Reader` and `io.ReadCloser`) with overwrites. - Load from multiple data sources(`[]byte`, file and `io.ReadCloser`) with overwrites.
- Read with recursion values. - Read with recursion values.
- Read with parent-child sections. - Read with parent-child sections.
- Read with auto-increment key names. - Read with auto-increment key names.
@ -24,19 +20,26 @@ Package ini provides INI file read and write functionality in Go.
## Installation ## Installation
The minimum requirement of Go is **1.13**. The minimum requirement of Go is **1.6**.
To use a tagged revision:
```sh ```sh
$ go get gopkg.in/ini.v1 $ go get gopkg.in/ini.v1
``` ```
To use with latest changes:
```sh
$ go get github.com/go-ini/ini
```
Please add `-u` flag to update in the future. Please add `-u` flag to update in the future.
## Getting Help ## Getting Help
- [Getting Started](https://ini.unknwon.io/docs/intro/getting_started) - [Getting Started](https://ini.unknwon.io/docs/intro/getting_started)
- [API Documentation](https://gowalker.org/gopkg.in/ini.v1) - [API Documentation](https://gowalker.org/gopkg.in/ini.v1)
- 中国大陆镜像https://ini.unknwon.cn
## License ## License

16
vendor/gopkg.in/ini.v1/codecov.yml generated vendored
View File

@ -1,16 +0,0 @@
coverage:
range: "60...95"
status:
project:
default:
threshold: 1%
informational: true
patch:
defualt:
only_pulls: true
informational: true
comment:
layout: 'diff'
github_checks: false

View File

@ -1,76 +0,0 @@
// Copyright 2019 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"
"fmt"
"io"
"io/ioutil"
"os"
)
var (
_ dataSource = (*sourceFile)(nil)
_ dataSource = (*sourceData)(nil)
_ dataSource = (*sourceReadCloser)(nil)
)
// dataSource is an interface that returns object which can be read and closed.
type dataSource interface {
ReadCloser() (io.ReadCloser, error)
}
// sourceFile represents an object that contains content on the local file system.
type sourceFile struct {
name string
}
func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
return os.Open(s.name)
}
// sourceData represents an object that contains content in memory.
type sourceData struct {
data []byte
}
func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
return ioutil.NopCloser(bytes.NewReader(s.data)), nil
}
// sourceReadCloser represents an input stream with Close method.
type sourceReadCloser struct {
reader io.ReadCloser
}
func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) {
return s.reader, nil
}
func parseDataSource(source interface{}) (dataSource, error) {
switch s := source.(type) {
case string:
return sourceFile{s}, nil
case []byte:
return &sourceData{s}, nil
case io.ReadCloser:
return &sourceReadCloser{s}, nil
case io.Reader:
return &sourceReadCloser{ioutil.NopCloser(s)}, nil
default:
return nil, fmt.Errorf("error parsing data source: unknown type %q", s)
}
}

22
vendor/gopkg.in/ini.v1/deprecated.go generated vendored
View File

@ -1,22 +0,0 @@
// Copyright 2019 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
var (
// Deprecated: Use "DefaultSection" instead.
DEFAULT_SECTION = DefaultSection
// Deprecated: AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE.
AllCapsUnderscore = SnackCase
)

15
vendor/gopkg.in/ini.v1/error.go generated vendored
View File

@ -32,18 +32,3 @@ func IsErrDelimiterNotFound(err error) bool {
func (err ErrDelimiterNotFound) Error() string { func (err ErrDelimiterNotFound) Error() string {
return fmt.Sprintf("key-value delimiter not found: %s", err.Line) return fmt.Sprintf("key-value delimiter not found: %s", err.Line)
} }
// ErrEmptyKeyName indicates the error type of no key name is found which there should be one.
type ErrEmptyKeyName struct {
Line string
}
// IsErrEmptyKeyName returns true if the given error is an instance of ErrEmptyKeyName.
func IsErrEmptyKeyName(err error) bool {
_, ok := err.(ErrEmptyKeyName)
return ok
}
func (err ErrEmptyKeyName) Error() string {
return fmt.Sprintf("empty key name: %s", err.Line)
}

209
vendor/gopkg.in/ini.v1/file.go generated vendored
View File

@ -25,7 +25,7 @@ import (
"sync" "sync"
) )
// File represents a combination of one or more INI files in memory. // File represents a combination of a or more INI file(s) in memory.
type File struct { type File struct {
options LoadOptions options LoadOptions
dataSources []dataSource dataSources []dataSource
@ -36,12 +36,8 @@ type File struct {
// To keep data in order. // To keep data in order.
sectionList []string sectionList []string
// To keep track of the index of a section with same name.
// This meta list is only used with non-unique section names are allowed.
sectionIndexes []int
// Actual data is stored here. // Actual data is stored here.
sections map[string][]*Section sections map[string]*Section
NameMapper NameMapper
ValueMapper ValueMapper
@ -52,40 +48,27 @@ func newFile(dataSources []dataSource, opts LoadOptions) *File {
if len(opts.KeyValueDelimiters) == 0 { if len(opts.KeyValueDelimiters) == 0 {
opts.KeyValueDelimiters = "=:" opts.KeyValueDelimiters = "=:"
} }
if len(opts.KeyValueDelimiterOnWrite) == 0 {
opts.KeyValueDelimiterOnWrite = "="
}
if len(opts.ChildSectionDelimiter) == 0 {
opts.ChildSectionDelimiter = "."
}
return &File{ return &File{
BlockMode: true, BlockMode: true,
dataSources: dataSources, dataSources: dataSources,
sections: make(map[string][]*Section), sections: make(map[string]*Section),
sectionList: make([]string, 0, 10),
options: opts, options: opts,
} }
} }
// Empty returns an empty file object. // Empty returns an empty file object.
func Empty(opts ...LoadOptions) *File { func Empty() *File {
var opt LoadOptions // Ignore error here, we sure our data is good.
if len(opts) > 0 { f, _ := Load([]byte(""))
opt = opts[0]
}
// Ignore error here, we are sure our data is good.
f, _ := LoadSources(opt, []byte(""))
return f return f
} }
// NewSection creates a new section. // NewSection creates a new section.
func (f *File) NewSection(name string) (*Section, error) { func (f *File) NewSection(name string) (*Section, error) {
if len(name) == 0 { if len(name) == 0 {
return nil, errors.New("empty section name") return nil, errors.New("error creating new section: empty section name")
} } else if f.options.Insensitive && name != DefaultSection {
if (f.options.Insensitive || f.options.InsensitiveSections) && name != DefaultSection {
name = strings.ToLower(name) name = strings.ToLower(name)
} }
@ -94,20 +77,13 @@ func (f *File) NewSection(name string) (*Section, error) {
defer f.lock.Unlock() defer f.lock.Unlock()
} }
if !f.options.AllowNonUniqueSections && inSlice(name, f.sectionList) { if inSlice(name, f.sectionList) {
return f.sections[name][0], nil return f.sections[name], nil
} }
f.sectionList = append(f.sectionList, name) f.sectionList = append(f.sectionList, name)
f.sections[name] = newSection(f, name)
// NOTE: Append to indexes must happen before appending to sections, return f.sections[name], nil
// otherwise index will have off-by-one problem.
f.sectionIndexes = append(f.sectionIndexes, len(f.sections[name]))
sec := newSection(f, name)
f.sections[name] = append(f.sections[name], sec)
return sec, nil
} }
// NewRawSection creates a new section with an unparseable body. // NewRawSection creates a new section with an unparseable body.
@ -134,26 +110,10 @@ func (f *File) NewSections(names ...string) (err error) {
// GetSection returns section by given name. // GetSection returns section by given name.
func (f *File) GetSection(name string) (*Section, error) { func (f *File) GetSection(name string) (*Section, error) {
secs, err := f.SectionsByName(name)
if err != nil {
return nil, err
}
return secs[0], err
}
// HasSection returns true if the file contains a section with given name.
func (f *File) HasSection(name string) bool {
section, _ := f.GetSection(name)
return section != nil
}
// SectionsByName returns all sections with given name.
func (f *File) SectionsByName(name string) ([]*Section, error) {
if len(name) == 0 { if len(name) == 0 {
name = DefaultSection name = DefaultSection
} }
if f.options.Insensitive || f.options.InsensitiveSections { if f.options.Insensitive {
name = strings.ToLower(name) name = strings.ToLower(name)
} }
@ -162,40 +122,25 @@ func (f *File) SectionsByName(name string) ([]*Section, error) {
defer f.lock.RUnlock() defer f.lock.RUnlock()
} }
secs := f.sections[name] sec := f.sections[name]
if len(secs) == 0 { if sec == nil {
return nil, fmt.Errorf("section %q does not exist", name) return nil, fmt.Errorf("section '%s' does not exist", name)
} }
return sec, nil
return secs, nil
} }
// Section assumes named section exists and returns a zero-value when not. // Section assumes named section exists and returns a zero-value when not.
func (f *File) Section(name string) *Section { func (f *File) Section(name string) *Section {
sec, err := f.GetSection(name) sec, err := f.GetSection(name)
if err != nil { if err != nil {
if name == "" { // Note: It's OK here because the only possible error is empty section name,
name = DefaultSection // but if it's empty, this piece of code won't be executed.
}
sec, _ = f.NewSection(name) sec, _ = f.NewSection(name)
return sec return sec
} }
return sec return sec
} }
// SectionWithIndex assumes named section exists and returns a new section when not.
func (f *File) SectionWithIndex(name string, index int) *Section {
secs, err := f.SectionsByName(name)
if err != nil || len(secs) <= index {
// NOTE: It's OK here because the only possible error is empty section name,
// but if it's empty, this piece of code won't be executed.
newSec, _ := f.NewSection(name)
return newSec
}
return secs[index]
}
// Sections returns a list of Section stored in the current instance. // Sections returns a list of Section stored in the current instance.
func (f *File) Sections() []*Section { func (f *File) Sections() []*Section {
if f.BlockMode { if f.BlockMode {
@ -205,7 +150,7 @@ func (f *File) Sections() []*Section {
sections := make([]*Section, len(f.sectionList)) sections := make([]*Section, len(f.sectionList))
for i, name := range f.sectionList { for i, name := range f.sectionList {
sections[i] = f.sections[name][f.sectionIndexes[i]] sections[i] = f.sections[name]
} }
return sections return sections
} }
@ -222,70 +167,24 @@ func (f *File) SectionStrings() []string {
return list return list
} }
// DeleteSection deletes a section or all sections with given name. // DeleteSection deletes a section.
func (f *File) DeleteSection(name string) { func (f *File) DeleteSection(name string) {
secs, err := f.SectionsByName(name)
if err != nil {
return
}
for i := 0; i < len(secs); i++ {
// For non-unique sections, it is always needed to remove the first one so
// in the next iteration, the subsequent section continue having index 0.
// Ignoring the error as index 0 never returns an error.
_ = f.DeleteSectionWithIndex(name, 0)
}
}
// DeleteSectionWithIndex deletes a section with given name and index.
func (f *File) DeleteSectionWithIndex(name string, index int) error {
if !f.options.AllowNonUniqueSections && index != 0 {
return fmt.Errorf("delete section with non-zero index is only allowed when non-unique sections is enabled")
}
if len(name) == 0 {
name = DefaultSection
}
if f.options.Insensitive || f.options.InsensitiveSections {
name = strings.ToLower(name)
}
if f.BlockMode { if f.BlockMode {
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
} }
// Count occurrences of the sections if len(name) == 0 {
occurrences := 0 name = DefaultSection
sectionListCopy := make([]string, len(f.sectionList))
copy(sectionListCopy, f.sectionList)
for i, s := range sectionListCopy {
if s != name {
continue
} }
if occurrences == index { for i, s := range f.sectionList {
if len(f.sections[name]) <= 1 { if s == name {
delete(f.sections, name) // The last one in the map
} else {
f.sections[name] = append(f.sections[name][:index], f.sections[name][index+1:]...)
}
// Fix section lists
f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...) f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
f.sectionIndexes = append(f.sectionIndexes[:i], f.sectionIndexes[i+1:]...) delete(f.sections, name)
return
} else if occurrences > index {
// Fix the indices of all following sections with this name.
f.sectionIndexes[i-1]--
} }
occurrences++
} }
return nil
} }
func (f *File) reload(s dataSource) error { func (f *File) reload(s dataSource) error {
@ -304,14 +203,11 @@ func (f *File) Reload() (err error) {
if err = f.reload(s); err != nil { if err = f.reload(s); err != nil {
// In loose mode, we create an empty default section for nonexistent files. // In loose mode, we create an empty default section for nonexistent files.
if os.IsNotExist(err) && f.options.Loose { if os.IsNotExist(err) && f.options.Loose {
_ = f.parse(bytes.NewBuffer(nil)) f.parse(bytes.NewBuffer(nil))
continue continue
} }
return err return err
} }
if f.options.ShortCircuit {
return nil
}
} }
return nil return nil
} }
@ -334,17 +230,16 @@ func (f *File) Append(source interface{}, others ...interface{}) error {
} }
func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
equalSign := DefaultFormatLeft + f.options.KeyValueDelimiterOnWrite + DefaultFormatRight equalSign := DefaultFormatLeft + "=" + DefaultFormatRight
if PrettyFormat || PrettyEqual { if PrettyFormat || PrettyEqual {
equalSign = fmt.Sprintf(" %s ", f.options.KeyValueDelimiterOnWrite) equalSign = " = "
} }
// Use buffer to make sure target is safe until finish encoding. // Use buffer to make sure target is safe until finish encoding.
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
lastSectionIdx := len(f.sectionList) - 1
for i, sname := range f.sectionList { for i, sname := range f.sectionList {
sec := f.SectionWithIndex(sname, f.sectionIndexes[i]) sec := f.Section(sname)
if len(sec.Comment) > 0 { if len(sec.Comment) > 0 {
// Support multiline comments // Support multiline comments
lines := strings.Split(sec.Comment, LineBreak) lines := strings.Split(sec.Comment, LineBreak)
@ -361,7 +256,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
} }
} }
if i > 0 || DefaultHeader || (i == 0 && strings.ToUpper(sec.name) != DefaultSection) { if i > 0 || DefaultHeader {
if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil { if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
return nil, err return nil, err
} }
@ -372,13 +267,12 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
} }
} }
isLastSection := i == lastSectionIdx
if sec.isRawSection { if sec.isRawSection {
if _, err := buf.WriteString(sec.rawBody); err != nil { if _, err := buf.WriteString(sec.rawBody); err != nil {
return nil, err return nil, err
} }
if PrettySection && !isLastSection { if PrettySection {
// Put a line between sections // Put a line between sections
if _, err := buf.WriteString(LineBreak); err != nil { if _, err := buf.WriteString(LineBreak); err != nil {
return nil, err return nil, err
@ -388,7 +282,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
} }
// Count and generate alignment length and buffer spaces using the // Count and generate alignment length and buffer spaces using the
// longest key. Keys may be modified if they contain certain characters so // longest key. Keys may be modifed if they contain certain characters so
// we need to take that into account in our calculation. // we need to take that into account in our calculation.
alignLength := 0 alignLength := 0
if PrettyFormat { if PrettyFormat {
@ -408,7 +302,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
} }
alignSpaces := bytes.Repeat([]byte(" "), alignLength) alignSpaces := bytes.Repeat([]byte(" "), alignLength)
KeyList: KEY_LIST:
for _, kname := range sec.keyList { for _, kname := range sec.keyList {
key := sec.Key(kname) key := sec.Key(kname)
if len(key.Comment) > 0 { if len(key.Comment) > 0 {
@ -444,14 +338,16 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
kname = `"""` + kname + `"""` kname = `"""` + kname + `"""`
} }
writeKeyValue := func(val string) (bool, error) { for _, val := range key.ValueWithShadows() {
if _, err := buf.WriteString(kname); err != nil { if _, err := buf.WriteString(kname); err != nil {
return false, err return nil, err
} }
if key.isBooleanType { if key.isBooleanType {
if kname != sec.keyList[len(sec.keyList)-1] {
buf.WriteString(LineBreak) buf.WriteString(LineBreak)
return true, nil }
continue KEY_LIST
} }
// Write out alignment spaces before "=" sign // Write out alignment spaces before "=" sign
@ -464,31 +360,12 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
val = `"""` + val + `"""` val = `"""` + val + `"""`
} else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") { } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
val = "`" + val + "`" val = "`" + val + "`"
} else if len(strings.TrimSpace(val)) != len(val) {
val = `"` + val + `"`
} }
if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil { if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil {
return false, err
}
return false, nil
}
shadows := key.ValueWithShadows()
if len(shadows) == 0 {
if _, err := writeKeyValue(""); err != nil {
return nil, err return nil, err
} }
} }
for _, val := range shadows {
exitLoop, err := writeKeyValue(val)
if err != nil {
return nil, err
} else if exitLoop {
continue KeyList
}
}
for _, val := range key.nestedValues { for _, val := range key.nestedValues {
if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil { if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil {
return nil, err return nil, err
@ -496,7 +373,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
} }
} }
if PrettySection && !isLastSection { if PrettySection {
// Put a line between sections // Put a line between sections
if _, err := buf.WriteString(LineBreak); err != nil { if _, err := buf.WriteString(LineBreak); err != nil {
return nil, err return nil, err
@ -526,7 +403,7 @@ func (f *File) WriteTo(w io.Writer) (int64, error) {
// SaveToIndent writes content to file system with given value indention. // SaveToIndent writes content to file system with given value indention.
func (f *File) SaveToIndent(filename, indent string) error { func (f *File) SaveToIndent(filename, indent string) error {
// Note: Because we are truncating with os.Create, // Note: Because we are truncating with os.Create,
// so it's safer to save to a temporary file location and rename after done. // so it's safer to save to a temporary file location and rename afte done.
buf, err := f.writeToBuffer(indent) buf, err := f.writeToBuffer(indent)
if err != nil { if err != nil {
return err return err

24
vendor/gopkg.in/ini.v1/helper.go generated vendored
View File

@ -1,24 +0,0 @@
// Copyright 2019 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
func inSlice(str string, s []string) bool {
for _, v := range s {
if str == v {
return true
}
}
return false
}

129
vendor/gopkg.in/ini.v1/ini.go generated vendored
View File

@ -1,3 +1,5 @@
// +build go1.6
// Copyright 2014 Unknwon // Copyright 2014 Unknwon
// //
// Licensed under the Apache License, Version 2.0 (the "License"): you may // Licensed under the Apache License, Version 2.0 (the "License"): you may
@ -16,71 +18,131 @@
package ini package ini
import ( import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os" "os"
"regexp" "regexp"
"runtime" "runtime"
"strings"
) )
const ( const (
// Maximum allowed depth when recursively substituing variable names. // DefaultSection is the name of default section. You can use this constant or the string literal.
depthValues = 99
)
var (
// DefaultSection is the name of default section. You can use this var or the string literal.
// In most of cases, an empty string is all you need to access the section. // In most of cases, an empty string is all you need to access the section.
DefaultSection = "DEFAULT" DefaultSection = "DEFAULT"
// Deprecated: Use "DefaultSection" instead.
DEFAULT_SECTION = DefaultSection
// Maximum allowed depth when recursively substituing variable names.
depthValues = 99
version = "1.44.0"
)
// Version returns current package version literal.
func Version() string {
return version
}
var (
// LineBreak is the delimiter to determine or compose a new line. // LineBreak is the delimiter to determine or compose a new line.
// This variable will be changed to "\r\n" automatically on Windows at package init time. // This variable will be changed to "\r\n" automatically on Windows at package init time.
LineBreak = "\n" LineBreak = "\n"
// DefaultFormatLeft places custom spaces on the left when PrettyFormat and PrettyEqual are both disabled.
DefaultFormatLeft = ""
// DefaultFormatRight places custom spaces on the right when PrettyFormat and PrettyEqual are both disabled.
DefaultFormatRight = ""
// Variable regexp pattern: %(variable)s // Variable regexp pattern: %(variable)s
varPattern = regexp.MustCompile(`%\(([^)]+)\)s`) varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`)
// PrettyFormat indicates whether to align "=" sign with spaces to produce pretty output
// or reduce all possible spaces for compact format.
PrettyFormat = true
// PrettyEqual places spaces around "=" sign even when PrettyFormat is false.
PrettyEqual = false
// DefaultHeader explicitly writes default section header. // DefaultHeader explicitly writes default section header.
DefaultHeader = false DefaultHeader = false
// PrettySection indicates whether to put a line between sections. // PrettySection indicates whether to put a line between sections.
PrettySection = true PrettySection = true
// PrettyFormat indicates whether to align "=" sign with spaces to produce pretty output
// or reduce all possible spaces for compact format.
PrettyFormat = true
// PrettyEqual places spaces around "=" sign even when PrettyFormat is false.
PrettyEqual = false
// DefaultFormatLeft places custom spaces on the left when PrettyFormat and PrettyEqual are both disabled.
DefaultFormatLeft = ""
// DefaultFormatRight places custom spaces on the right when PrettyFormat and PrettyEqual are both disabled.
DefaultFormatRight = ""
) )
var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test")
func init() { func init() {
if runtime.GOOS == "windows" && !inTest { if runtime.GOOS == "windows" {
LineBreak = "\r\n" LineBreak = "\r\n"
} }
} }
func inSlice(str string, s []string) bool {
for _, v := range s {
if str == v {
return true
}
}
return false
}
// dataSource is an interface that returns object which can be read and closed.
type dataSource interface {
ReadCloser() (io.ReadCloser, error)
}
// sourceFile represents an object that contains content on the local file system.
type sourceFile struct {
name string
}
func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
return os.Open(s.name)
}
// sourceData represents an object that contains content in memory.
type sourceData struct {
data []byte
}
func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
return ioutil.NopCloser(bytes.NewReader(s.data)), nil
}
// sourceReadCloser represents an input stream with Close method.
type sourceReadCloser struct {
reader io.ReadCloser
}
func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) {
return s.reader, nil
}
func parseDataSource(source interface{}) (dataSource, error) {
switch s := source.(type) {
case string:
return sourceFile{s}, nil
case []byte:
return &sourceData{s}, nil
case io.ReadCloser:
return &sourceReadCloser{s}, nil
default:
return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s)
}
}
// LoadOptions contains all customized options used for load data source(s). // LoadOptions contains all customized options used for load data source(s).
type LoadOptions struct { type LoadOptions struct {
// Loose indicates whether the parser should ignore nonexistent files or return error. // Loose indicates whether the parser should ignore nonexistent files or return error.
Loose bool Loose bool
// Insensitive indicates whether the parser forces all section and key names to lowercase. // Insensitive indicates whether the parser forces all section and key names to lowercase.
Insensitive bool Insensitive bool
// InsensitiveSections indicates whether the parser forces all section to lowercase.
InsensitiveSections bool
// InsensitiveKeys indicates whether the parser forces all key names to lowercase.
InsensitiveKeys bool
// IgnoreContinuation indicates whether to ignore continuation lines while parsing. // IgnoreContinuation indicates whether to ignore continuation lines while parsing.
IgnoreContinuation bool IgnoreContinuation bool
// IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value. // IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value.
IgnoreInlineComment bool IgnoreInlineComment bool
// SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs. // SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs.
SkipUnrecognizableLines bool SkipUnrecognizableLines bool
// ShortCircuit indicates whether to ignore other configuration sources after loaded the first available configuration source.
ShortCircuit bool
// AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing. // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
// This type of keys are mostly used in my.cnf. // This type of keys are mostly used in my.cnf.
AllowBooleanKeys bool AllowBooleanKeys bool
@ -111,25 +173,10 @@ type LoadOptions struct {
UnparseableSections []string UnparseableSections []string
// KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:". // KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:".
KeyValueDelimiters string KeyValueDelimiters string
// KeyValueDelimiterOnWrite is the delimiter that are used to separate key and value output. By default, it is "=".
KeyValueDelimiterOnWrite string
// ChildSectionDelimiter is the delimiter that is used to separate child sections. By default, it is ".".
ChildSectionDelimiter string
// PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes). // PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes).
PreserveSurroundedQuote bool PreserveSurroundedQuote bool
// DebugFunc is called to collect debug information (currently only useful to debug parsing Python-style multiline values).
DebugFunc DebugFunc
// ReaderBufferSize is the buffer size of the reader in bytes.
ReaderBufferSize int
// AllowNonUniqueSections indicates whether to allow sections with the same name multiple times.
AllowNonUniqueSections bool
// AllowDuplicateShadowValues indicates whether values for shadowed keys should be deduplicated.
AllowDuplicateShadowValues bool
} }
// DebugFunc is the type of function called to log parse events.
type DebugFunc func(message string)
// LoadSources allows caller to apply customized options for loading from data source(s). // LoadSources allows caller to apply customized options for loading from data source(s).
func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) { func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) {
sources := make([]dataSource, len(others)+1) sources := make([]dataSource, len(others)+1)

164
vendor/gopkg.in/ini.v1/key.go generated vendored
View File

@ -54,18 +54,6 @@ func (k *Key) addShadow(val string) error {
return errors.New("cannot add shadow to auto-increment or boolean key") return errors.New("cannot add shadow to auto-increment or boolean key")
} }
if !k.s.f.options.AllowDuplicateShadowValues {
// Deduplicate shadows based on their values.
if k.value == val {
return nil
}
for i := range k.shadows {
if k.shadows[i].value == val {
return nil
}
}
}
shadow := newKey(k.s, k.name, val) shadow := newKey(k.s, k.name, val)
shadow.isShadow = true shadow.isShadow = true
k.shadows = append(k.shadows, shadow) k.shadows = append(k.shadows, shadow)
@ -110,24 +98,15 @@ func (k *Key) Value() string {
return k.value return k.value
} }
// ValueWithShadows returns raw values of key and its shadows if any. Shadow // ValueWithShadows returns raw values of key and its shadows if any.
// keys with empty values are ignored from the returned list.
func (k *Key) ValueWithShadows() []string { func (k *Key) ValueWithShadows() []string {
if len(k.shadows) == 0 { if len(k.shadows) == 0 {
if k.value == "" {
return []string{}
}
return []string{k.value} return []string{k.value}
} }
vals := make([]string, len(k.shadows)+1)
vals := make([]string, 0, len(k.shadows)+1) vals[0] = k.value
if k.value != "" { for i := range k.shadows {
vals = append(vals, k.value) vals[i+1] = k.shadows[i].value
}
for _, s := range k.shadows {
if s.value != "" {
vals = append(vals, s.value)
}
} }
return vals return vals
} }
@ -158,15 +137,10 @@ func (k *Key) transformValue(val string) string {
noption := vr[2 : len(vr)-2] noption := vr[2 : len(vr)-2]
// Search in the same section. // Search in the same section.
// If not found or found the key itself, then search again in default section.
nk, err := k.s.GetKey(noption) nk, err := k.s.GetKey(noption)
if err != nil || k == nk { if err != nil || k == nk {
// Search again in default section.
nk, _ = k.s.f.Section("").GetKey(noption) nk, _ = k.s.f.Section("").GetKey(noption)
if nk == nil {
// Stop when no results found in the default section,
// and returns the value as-is.
break
}
} }
// Substitute by new value and take off leading '%(' and trailing ')s'. // Substitute by new value and take off leading '%(' and trailing ')s'.
@ -580,12 +554,6 @@ func (k *Key) Uint64s(delim string) []uint64 {
return vals return vals
} }
// Bools returns list of bool divided by given delimiter. Any invalid input will be treated as zero value.
func (k *Key) Bools(delim string) []bool {
vals, _ := k.parseBools(k.Strings(delim), true, false)
return vals
}
// TimesFormat parses with given format and returns list of time.Time divided by given delimiter. // TimesFormat parses with given format and returns list of time.Time divided by given delimiter.
// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC). // Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC).
func (k *Key) TimesFormat(format, delim string) []time.Time { func (k *Key) TimesFormat(format, delim string) []time.Time {
@ -634,13 +602,6 @@ func (k *Key) ValidUint64s(delim string) []uint64 {
return vals return vals
} }
// ValidBools returns list of bool divided by given delimiter. If some value is not 64-bit unsigned
// integer, then it will not be included to result list.
func (k *Key) ValidBools(delim string) []bool {
vals, _ := k.parseBools(k.Strings(delim), false, false)
return vals
}
// ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter. // ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter.
func (k *Key) ValidTimesFormat(format, delim string) []time.Time { func (k *Key) ValidTimesFormat(format, delim string) []time.Time {
vals, _ := k.parseTimesFormat(format, k.Strings(delim), false, false) vals, _ := k.parseTimesFormat(format, k.Strings(delim), false, false)
@ -677,11 +638,6 @@ func (k *Key) StrictUint64s(delim string) ([]uint64, error) {
return k.parseUint64s(k.Strings(delim), false, true) return k.parseUint64s(k.Strings(delim), false, true)
} }
// StrictBools returns list of bool divided by given delimiter or error on first invalid input.
func (k *Key) StrictBools(delim string) ([]bool, error) {
return k.parseBools(k.Strings(delim), false, true)
}
// StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter // StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter
// or error on first invalid input. // or error on first invalid input.
func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error) { func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error) {
@ -694,127 +650,87 @@ func (k *Key) StrictTimes(delim string) ([]time.Time, error) {
return k.StrictTimesFormat(time.RFC3339, delim) return k.StrictTimesFormat(time.RFC3339, delim)
} }
// parseBools transforms strings to bools.
func (k *Key) parseBools(strs []string, addInvalid, returnOnInvalid bool) ([]bool, error) {
vals := make([]bool, 0, len(strs))
parser := func(str string) (interface{}, error) {
val, err := parseBool(str)
return val, err
}
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
if err == nil {
for _, val := range rawVals {
vals = append(vals, val.(bool))
}
}
return vals, err
}
// parseFloat64s transforms strings to float64s. // parseFloat64s transforms strings to float64s.
func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]float64, error) { func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]float64, error) {
vals := make([]float64, 0, len(strs)) vals := make([]float64, 0, len(strs))
parser := func(str string) (interface{}, error) { for _, str := range strs {
val, err := strconv.ParseFloat(str, 64) val, err := strconv.ParseFloat(str, 64)
return val, err if err != nil && returnOnInvalid {
return nil, err
} }
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) if err == nil || addInvalid {
if err == nil { vals = append(vals, val)
for _, val := range rawVals {
vals = append(vals, val.(float64))
} }
} }
return vals, err return vals, nil
} }
// parseInts transforms strings to ints. // parseInts transforms strings to ints.
func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) { func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) {
vals := make([]int, 0, len(strs)) vals := make([]int, 0, len(strs))
parser := func(str string) (interface{}, error) { for _, str := range strs {
val, err := strconv.ParseInt(str, 0, 64) valInt64, err := strconv.ParseInt(str, 0, 64)
return val, err val := int(valInt64)
if err != nil && returnOnInvalid {
return nil, err
} }
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) if err == nil || addInvalid {
if err == nil { vals = append(vals, val)
for _, val := range rawVals {
vals = append(vals, int(val.(int64)))
} }
} }
return vals, err return vals, nil
} }
// parseInt64s transforms strings to int64s. // parseInt64s transforms strings to int64s.
func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) { func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) {
vals := make([]int64, 0, len(strs)) vals := make([]int64, 0, len(strs))
parser := func(str string) (interface{}, error) { for _, str := range strs {
val, err := strconv.ParseInt(str, 0, 64) val, err := strconv.ParseInt(str, 0, 64)
return val, err if err != nil && returnOnInvalid {
return nil, err
} }
if err == nil || addInvalid {
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) vals = append(vals, val)
if err == nil {
for _, val := range rawVals {
vals = append(vals, val.(int64))
} }
} }
return vals, err return vals, nil
} }
// parseUints transforms strings to uints. // parseUints transforms strings to uints.
func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uint, error) { func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uint, error) {
vals := make([]uint, 0, len(strs)) vals := make([]uint, 0, len(strs))
parser := func(str string) (interface{}, error) { for _, str := range strs {
val, err := strconv.ParseUint(str, 0, 64) val, err := strconv.ParseUint(str, 0, 0)
return val, err if err != nil && returnOnInvalid {
return nil, err
} }
if err == nil || addInvalid {
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) vals = append(vals, uint(val))
if err == nil {
for _, val := range rawVals {
vals = append(vals, uint(val.(uint64)))
} }
} }
return vals, err return vals, nil
} }
// parseUint64s transforms strings to uint64s. // parseUint64s transforms strings to uint64s.
func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]uint64, error) { func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]uint64, error) {
vals := make([]uint64, 0, len(strs)) vals := make([]uint64, 0, len(strs))
parser := func(str string) (interface{}, error) { for _, str := range strs {
val, err := strconv.ParseUint(str, 0, 64) val, err := strconv.ParseUint(str, 0, 64)
return val, err if err != nil && returnOnInvalid {
return nil, err
} }
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) if err == nil || addInvalid {
if err == nil { vals = append(vals, val)
for _, val := range rawVals {
vals = append(vals, val.(uint64))
} }
} }
return vals, err return vals, nil
} }
type Parser func(str string) (interface{}, error)
// parseTimesFormat transforms strings to times in given format. // parseTimesFormat transforms strings to times in given format.
func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) { func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) {
vals := make([]time.Time, 0, len(strs)) vals := make([]time.Time, 0, len(strs))
parser := func(str string) (interface{}, error) {
val, err := time.Parse(format, str)
return val, err
}
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
if err == nil {
for _, val := range rawVals {
vals = append(vals, val.(time.Time))
}
}
return vals, err
}
// doParse transforms strings to different types
func (k *Key) doParse(strs []string, addInvalid, returnOnInvalid bool, parser Parser) ([]interface{}, error) {
vals := make([]interface{}, 0, len(strs))
for _, str := range strs { for _, str := range strs {
val, err := parser(str) val, err := time.Parse(format, str)
if err != nil && returnOnInvalid { if err != nil && returnOnInvalid {
return nil, err return nil, err
} }

101
vendor/gopkg.in/ini.v1/parser.go generated vendored
View File

@ -25,9 +25,7 @@ import (
"unicode" "unicode"
) )
const minReaderBufferSize = 4096 var pythonMultiline = regexp.MustCompile("^(\\s+)([^\n]+)")
var pythonMultiline = regexp.MustCompile(`^([\t\f ]+)(.*)`)
type parserOptions struct { type parserOptions struct {
IgnoreContinuation bool IgnoreContinuation bool
@ -37,8 +35,6 @@ type parserOptions struct {
UnescapeValueDoubleQuotes bool UnescapeValueDoubleQuotes bool
UnescapeValueCommentSymbols bool UnescapeValueCommentSymbols bool
PreserveSurroundedQuote bool PreserveSurroundedQuote bool
DebugFunc DebugFunc
ReaderBufferSize int
} }
type parser struct { type parser struct {
@ -50,20 +46,9 @@ type parser struct {
comment *bytes.Buffer 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 { func newParser(r io.Reader, opts parserOptions) *parser {
size := opts.ReaderBufferSize
if size < minReaderBufferSize {
size = minReaderBufferSize
}
return &parser{ return &parser{
buf: bufio.NewReaderSize(r, size), buf: bufio.NewReader(r),
options: opts, options: opts,
count: 1, count: 1,
comment: &bytes.Buffer{}, comment: &bytes.Buffer{},
@ -84,10 +69,7 @@ func (p *parser) BOM() error {
case mask[0] == 254 && mask[1] == 255: case mask[0] == 254 && mask[1] == 255:
fallthrough fallthrough
case mask[0] == 255 && mask[1] == 254: case mask[0] == 255 && mask[1] == 254:
_, err = p.buf.Read(mask) p.buf.Read(mask)
if err != nil {
return err
}
case mask[0] == 239 && mask[1] == 187: case mask[0] == 239 && mask[1] == 187:
mask, err := p.buf.Peek(3) mask, err := p.buf.Peek(3)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
@ -96,10 +78,7 @@ func (p *parser) BOM() error {
return nil return nil
} }
if mask[2] == 191 { if mask[2] == 191 {
_, err = p.buf.Read(mask) p.buf.Read(mask)
if err != nil {
return err
}
} }
} }
return nil return nil
@ -131,7 +110,7 @@ func readKeyName(delimiters string, in []byte) (string, int, error) {
// Check if key name surrounded by quotes. // Check if key name surrounded by quotes.
var keyQuote string var keyQuote string
if line[0] == '"' { if line[0] == '"' {
if len(line) > 6 && line[0:3] == `"""` { if len(line) > 6 && string(line[0:3]) == `"""` {
keyQuote = `"""` keyQuote = `"""`
} else { } else {
keyQuote = `"` keyQuote = `"`
@ -141,7 +120,7 @@ func readKeyName(delimiters string, in []byte) (string, int, error) {
} }
// Get out key name // Get out key name
var endIdx int endIdx := -1
if len(keyQuote) > 0 { if len(keyQuote) > 0 {
startIdx := len(keyQuote) startIdx := len(keyQuote)
// FIXME: fail case -> """"""name"""=value // FIXME: fail case -> """"""name"""=value
@ -164,10 +143,6 @@ func readKeyName(delimiters string, in []byte) (string, int, error) {
if endIdx < 0 { if endIdx < 0 {
return "", -1, ErrDelimiterNotFound{line} return "", -1, ErrDelimiterNotFound{line}
} }
if endIdx == 0 {
return "", -1, ErrEmptyKeyName{line}
}
return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil
} }
@ -191,7 +166,7 @@ func (p *parser) readMultilines(line, val, valQuote string) (string, error) {
} }
val += next val += next
if p.isEOF { if p.isEOF {
return "", fmt.Errorf("missing closing key quote from %q to %q", line, next) return "", fmt.Errorf("missing closing key quote from '%s' to '%s'", line, next)
} }
} }
return val, nil return val, nil
@ -236,7 +211,7 @@ func (p *parser) readValue(in []byte, bufferSize int) (string, error) {
} }
var valQuote string var valQuote string
if len(line) > 3 && line[0:3] == `"""` { if len(line) > 3 && string(line[0:3]) == `"""` {
valQuote = `"""` valQuote = `"""`
} else if line[0] == '`' { } else if line[0] == '`' {
valQuote = "`" valQuote = "`"
@ -293,8 +268,12 @@ func (p *parser) readValue(in []byte, bufferSize int) (string, error) {
hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote { hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote {
line = line[1 : len(line)-1] line = line[1 : len(line)-1]
} else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols { } else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols {
line = strings.ReplaceAll(line, `\;`, ";") if strings.Contains(line, `\;`) {
line = strings.ReplaceAll(line, `\#`, "#") line = strings.Replace(line, `\;`, ";", -1)
}
if strings.Contains(line, `\#`) {
line = strings.Replace(line, `\#`, "#", -1)
}
} else if p.options.AllowPythonMultilineValues && lastChar == '\n' { } else if p.options.AllowPythonMultilineValues && lastChar == '\n' {
return p.readPythonMultilines(line, bufferSize) return p.readPythonMultilines(line, bufferSize)
} }
@ -308,33 +287,31 @@ func (p *parser) readPythonMultilines(line string, bufferSize int) (string, erro
for { for {
peekData, peekErr := peekBuffer.ReadBytes('\n') peekData, peekErr := peekBuffer.ReadBytes('\n')
if peekErr != nil && peekErr != io.EOF { if peekErr != nil {
p.debug("readPythonMultilines: failed to peek with error: %v", peekErr) if peekErr == io.EOF {
return line, nil
}
return "", peekErr return "", peekErr
} }
p.debug("readPythonMultilines: parsing %q", string(peekData))
peekMatches := pythonMultiline.FindStringSubmatch(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 { if len(peekMatches) != 3 {
p.debug("readPythonMultilines: end of value, got: %q", line)
return line, nil return line, nil
} }
// Advance the parser reader (buffer) in-sync with the peek buffer. // NOTE: Return if not a python-ini multi-line value.
_, err := p.buf.Discard(len(peekData)) currentIdentSize := len(peekMatches[1])
if currentIdentSize <= 0 {
return line, nil
}
// NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer.
_, err := p.readUntil('\n')
if err != nil { if err != nil {
p.debug("readPythonMultilines: failed to skip to the end, returning error")
return "", err return "", err
} }
line += "\n" + peekMatches[0] line += fmt.Sprintf("\n%s", peekMatches[2])
} }
} }
@ -348,8 +325,6 @@ func (f *File) parse(reader io.Reader) (err error) {
UnescapeValueDoubleQuotes: f.options.UnescapeValueDoubleQuotes, UnescapeValueDoubleQuotes: f.options.UnescapeValueDoubleQuotes,
UnescapeValueCommentSymbols: f.options.UnescapeValueCommentSymbols, UnescapeValueCommentSymbols: f.options.UnescapeValueCommentSymbols,
PreserveSurroundedQuote: f.options.PreserveSurroundedQuote, PreserveSurroundedQuote: f.options.PreserveSurroundedQuote,
DebugFunc: f.options.DebugFunc,
ReaderBufferSize: f.options.ReaderBufferSize,
}) })
if err = p.BOM(); err != nil { if err = p.BOM(); err != nil {
return fmt.Errorf("BOM: %v", err) return fmt.Errorf("BOM: %v", err)
@ -357,7 +332,7 @@ func (f *File) parse(reader io.Reader) (err error) {
// Ignore error because default section name is never empty string. // Ignore error because default section name is never empty string.
name := DefaultSection name := DefaultSection
if f.options.Insensitive || f.options.InsensitiveSections { if f.options.Insensitive {
name = strings.ToLower(DefaultSection) name = strings.ToLower(DefaultSection)
} }
section, _ := f.NewSection(name) section, _ := f.NewSection(name)
@ -373,8 +348,8 @@ func (f *File) parse(reader io.Reader) (err error) {
// the size of the parser buffer is found. // 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()`. // TODO(unknwon): When Golang 1.10 is the lowest version supported, replace with `parserBufferSize := p.buf.Size()`.
parserBufferSize := 0 parserBufferSize := 0
// NOTE: Peek 4kb at a time. // NOTE: Peek 1kb at a time.
currentPeekSize := minReaderBufferSize currentPeekSize := 1024
if f.options.AllowPythonMultilineValues { if f.options.AllowPythonMultilineValues {
for { for {
@ -399,10 +374,7 @@ func (f *File) parse(reader io.Reader) (err error) {
if f.options.AllowNestedValues && if f.options.AllowNestedValues &&
isLastValueEmpty && len(line) > 0 { isLastValueEmpty && len(line) > 0 {
if line[0] == ' ' || line[0] == '\t' { if line[0] == ' ' || line[0] == '\t' {
err = lastRegularKey.addNestedValue(string(bytes.TrimSpace(line))) lastRegularKey.addNestedValue(string(bytes.TrimSpace(line)))
if err != nil {
return err
}
continue continue
} }
} }
@ -442,16 +414,14 @@ func (f *File) parse(reader io.Reader) (err error) {
section.Comment = strings.TrimSpace(p.comment.String()) section.Comment = strings.TrimSpace(p.comment.String())
// Reset auto-counter and comments // Reset aotu-counter and comments
p.comment.Reset() p.comment.Reset()
p.count = 1 p.count = 1
// Nested values can't span sections
isLastValueEmpty = false
inUnparseableSection = false inUnparseableSection = false
for i := range f.options.UnparseableSections { for i := range f.options.UnparseableSections {
if f.options.UnparseableSections[i] == name || if f.options.UnparseableSections[i] == name ||
((f.options.Insensitive || f.options.InsensitiveSections) && strings.EqualFold(f.options.UnparseableSections[i], name)) { (f.options.Insensitive && strings.ToLower(f.options.UnparseableSections[i]) == strings.ToLower(name)) {
inUnparseableSection = true inUnparseableSection = true
continue continue
} }
@ -467,9 +437,8 @@ func (f *File) parse(reader io.Reader) (err error) {
kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line) kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line)
if err != nil { if err != nil {
switch {
// Treat as boolean key when desired, and whole line is key name. // Treat as boolean key when desired, and whole line is key name.
case IsErrDelimiterNotFound(err): if IsErrDelimiterNotFound(err) {
switch { switch {
case f.options.AllowBooleanKeys: case f.options.AllowBooleanKeys:
kname, err := p.readValue(line, parserBufferSize) kname, err := p.readValue(line, parserBufferSize)
@ -487,8 +456,6 @@ func (f *File) parse(reader io.Reader) (err error) {
case f.options.SkipUnrecognizableLines: case f.options.SkipUnrecognizableLines:
continue continue
} }
case IsErrEmptyKeyName(err) && f.options.SkipUnrecognizableLines:
continue
} }
return err return err
} }

16
vendor/gopkg.in/ini.v1/section.go generated vendored
View File

@ -66,7 +66,7 @@ func (s *Section) SetBody(body string) {
func (s *Section) NewKey(name, val string) (*Key, error) { func (s *Section) NewKey(name, val string) (*Key, error) {
if len(name) == 0 { if len(name) == 0 {
return nil, errors.New("error creating new key: empty key name") return nil, errors.New("error creating new key: empty key name")
} else if s.f.options.Insensitive || s.f.options.InsensitiveKeys { } else if s.f.options.Insensitive {
name = strings.ToLower(name) name = strings.ToLower(name)
} }
@ -109,7 +109,7 @@ func (s *Section) GetKey(name string) (*Key, error) {
if s.f.BlockMode { if s.f.BlockMode {
s.f.lock.RLock() s.f.lock.RLock()
} }
if s.f.options.Insensitive || s.f.options.InsensitiveKeys { if s.f.options.Insensitive {
name = strings.ToLower(name) name = strings.ToLower(name)
} }
key := s.keys[name] key := s.keys[name]
@ -121,7 +121,7 @@ func (s *Section) GetKey(name string) (*Key, error) {
// Check if it is a child-section. // Check if it is a child-section.
sname := s.name sname := s.name
for { for {
if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 { if i := strings.LastIndex(sname, "."); i > -1 {
sname = sname[:i] sname = sname[:i]
sec, err := s.f.GetSection(sname) sec, err := s.f.GetSection(sname)
if err != nil { if err != nil {
@ -131,7 +131,7 @@ func (s *Section) GetKey(name string) (*Key, error) {
} }
break break
} }
return nil, fmt.Errorf("error when getting key of section %q: key %q not exists", s.name, name) return nil, fmt.Errorf("error when getting key of section '%s': key '%s' not exists", s.name, name)
} }
return key, nil return key, nil
} }
@ -188,7 +188,7 @@ func (s *Section) ParentKeys() []*Key {
var parentKeys []*Key var parentKeys []*Key
sname := s.name sname := s.name
for { for {
if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 { if i := strings.LastIndex(sname, "."); i > -1 {
sname = sname[:i] sname = sname[:i]
sec, err := s.f.GetSection(sname) sec, err := s.f.GetSection(sname)
if err != nil { if err != nil {
@ -217,7 +217,7 @@ func (s *Section) KeysHash() map[string]string {
defer s.f.lock.RUnlock() defer s.f.lock.RUnlock()
} }
hash := make(map[string]string, len(s.keysHash)) hash := map[string]string{}
for key, value := range s.keysHash { for key, value := range s.keysHash {
hash[key] = value hash[key] = value
} }
@ -245,11 +245,11 @@ func (s *Section) DeleteKey(name string) {
// For example, "[parent.child1]" and "[parent.child12]" are child sections // For example, "[parent.child1]" and "[parent.child12]" are child sections
// of section "[parent]". // of section "[parent]".
func (s *Section) ChildSections() []*Section { func (s *Section) ChildSections() []*Section {
prefix := s.name + s.f.options.ChildSectionDelimiter prefix := s.name + "."
children := make([]*Section, 0, 3) children := make([]*Section, 0, 3)
for _, name := range s.f.sectionList { for _, name := range s.f.sectionList {
if strings.HasPrefix(name, prefix) { if strings.HasPrefix(name, prefix) {
children = append(children, s.f.sections[name]...) children = append(children, s.f.sections[name])
} }
} }
return children return children

311
vendor/gopkg.in/ini.v1/struct.go generated vendored
View File

@ -29,8 +29,8 @@ type NameMapper func(string) string
// Built-in name getters. // Built-in name getters.
var ( var (
// SnackCase converts to format SNACK_CASE. // AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE.
SnackCase NameMapper = func(raw string) string { AllCapsUnderscore NameMapper = func(raw string) string {
newstr := make([]rune, 0, len(raw)) newstr := make([]rune, 0, len(raw))
for i, chr := range raw { for i, chr := range raw {
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
@ -50,7 +50,7 @@ var (
if i > 0 { if i > 0 {
newstr = append(newstr, '_') newstr = append(newstr, '_')
} }
chr -= 'A' - 'a' chr -= ('A' - 'a')
} }
newstr = append(newstr, chr) newstr = append(newstr, chr)
} }
@ -108,8 +108,6 @@ func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowSh
vals, err = key.parseUint64s(strs, true, false) vals, err = key.parseUint64s(strs, true, false)
case reflect.Float64: case reflect.Float64:
vals, err = key.parseFloat64s(strs, true, false) vals, err = key.parseFloat64s(strs, true, false)
case reflect.Bool:
vals, err = key.parseBools(strs, true, false)
case reflectTime: case reflectTime:
vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false) vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false)
default: default:
@ -134,8 +132,6 @@ func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowSh
slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i])) slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i]))
case reflect.Float64: case reflect.Float64:
slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i])) slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i]))
case reflect.Bool:
slice.Index(i).Set(reflect.ValueOf(vals.([]bool)[i]))
case reflectTime: case reflectTime:
slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i])) slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i]))
} }
@ -155,45 +151,23 @@ func wrapStrictError(err error, isStrict bool) error {
// but it does not return error for failing parsing, // but it does not return error for failing parsing,
// because we want to use default value that is already assigned to struct. // 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 { func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
vt := t switch t.Kind() {
isPtr := t.Kind() == reflect.Ptr
if isPtr {
vt = t.Elem()
}
switch vt.Kind() {
case reflect.String: case reflect.String:
stringVal := key.String() if len(key.String()) == 0 {
if isPtr { return nil
field.Set(reflect.ValueOf(&stringVal))
} else if len(stringVal) > 0 {
field.SetString(key.String())
} }
field.SetString(key.String())
case reflect.Bool: case reflect.Bool:
boolVal, err := key.Bool() boolVal, err := key.Bool()
if err != nil { if err != nil {
return wrapStrictError(err, isStrict) return wrapStrictError(err, isStrict)
} }
if isPtr {
field.Set(reflect.ValueOf(&boolVal))
} else {
field.SetBool(boolVal) field.SetBool(boolVal)
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 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() durationVal, err := key.Duration()
if err != nil { // Skip zero value
if intVal, err := key.Int64(); err == nil { if err == nil && int64(durationVal) > 0 {
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)) field.Set(reflect.ValueOf(durationVal))
}
return nil return nil
} }
@ -201,23 +175,13 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri
if err != nil { if err != nil {
return wrapStrictError(err, isStrict) return wrapStrictError(err, isStrict)
} }
if isPtr {
pv := reflect.New(t.Elem())
pv.Elem().SetInt(intVal)
field.Set(pv)
} else {
field.SetInt(intVal) field.SetInt(intVal)
}
// byte is an alias for uint8, so supporting uint8 breaks support for byte // byte is an alias for uint8, so supporting uint8 breaks support for byte
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
durationVal, err := key.Duration() durationVal, err := key.Duration()
// Skip zero value // Skip zero value
if err == nil && uint64(durationVal) > 0 { if err == nil && uint64(durationVal) > 0 {
if isPtr {
field.Set(reflect.ValueOf(&durationVal))
} else {
field.Set(reflect.ValueOf(durationVal)) field.Set(reflect.ValueOf(durationVal))
}
return nil return nil
} }
@ -225,59 +189,41 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri
if err != nil { if err != nil {
return wrapStrictError(err, isStrict) return wrapStrictError(err, isStrict)
} }
if isPtr {
pv := reflect.New(t.Elem())
pv.Elem().SetUint(uintVal)
field.Set(pv)
} else {
field.SetUint(uintVal) field.SetUint(uintVal)
}
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
floatVal, err := key.Float64() floatVal, err := key.Float64()
if err != nil { if err != nil {
return wrapStrictError(err, isStrict) return wrapStrictError(err, isStrict)
} }
if isPtr {
pv := reflect.New(t.Elem())
pv.Elem().SetFloat(floatVal)
field.Set(pv)
} else {
field.SetFloat(floatVal) field.SetFloat(floatVal)
}
case reflectTime: case reflectTime:
timeVal, err := key.Time() timeVal, err := key.Time()
if err != nil { if err != nil {
return wrapStrictError(err, isStrict) return wrapStrictError(err, isStrict)
} }
if isPtr {
field.Set(reflect.ValueOf(&timeVal))
} else {
field.Set(reflect.ValueOf(timeVal)) field.Set(reflect.ValueOf(timeVal))
}
case reflect.Slice: case reflect.Slice:
return setSliceWithProperType(key, field, delim, allowShadow, isStrict) return setSliceWithProperType(key, field, delim, allowShadow, isStrict)
default: default:
return fmt.Errorf("unsupported type %q", t) return fmt.Errorf("unsupported type '%s'", t)
} }
return nil return nil
} }
func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool, allowNonUnique bool, extends bool) { func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool) {
opts := strings.SplitN(tag, ",", 5) opts := strings.SplitN(tag, ",", 3)
rawName = opts[0] rawName = opts[0]
for _, opt := range opts[1:] { if len(opts) > 1 {
omitEmpty = omitEmpty || (opt == "omitempty") omitEmpty = opts[1] == "omitempty"
allowShadow = allowShadow || (opt == "allowshadow")
allowNonUnique = allowNonUnique || (opt == "nonunique")
extends = extends || (opt == "extends")
} }
return rawName, omitEmpty, allowShadow, allowNonUnique, extends if len(opts) > 2 {
allowShadow = opts[2] == "allowshadow"
}
return rawName, omitEmpty, allowShadow
} }
// mapToField maps the given value to the matching field of the given section. func (s *Section) mapTo(val reflect.Value, isStrict bool) error {
// 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 { if val.Kind() == reflect.Ptr {
val = val.Elem() val = val.Elem()
} }
@ -292,7 +238,7 @@ func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int,
continue continue
} }
rawName, _, allowShadow, allowNonUnique, extends := parseTagOptions(tag) rawName, _, allowShadow := parseTagOptions(tag)
fieldName := s.parseFieldName(tpField.Name, rawName) fieldName := s.parseFieldName(tpField.Name, rawName)
if len(fieldName) == 0 || !field.CanSet() { if len(fieldName) == 0 || !field.CanSet() {
continue continue
@ -300,116 +246,63 @@ func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int,
isStruct := tpField.Type.Kind() == reflect.Struct isStruct := tpField.Type.Kind() == reflect.Struct
isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct
isAnonymousPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
if isAnonymousPtr { if isAnonymous {
field.Set(reflect.New(tpField.Type.Elem())) field.Set(reflect.New(tpField.Type.Elem()))
} }
if extends && (isAnonymousPtr || (isStruct && tpField.Anonymous)) { if isAnonymous || isStruct || isStructPtr {
if sec, err := s.f.GetSection(fieldName); err == nil {
// 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() { if isStructPtr && field.IsNil() {
field.Set(reflect.New(tpField.Type.Elem())) field.Set(reflect.New(tpField.Type.Elem()))
} }
fieldSection := s if err = sec.mapTo(field, isStrict); err != nil {
if rawName != "" { return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
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 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 { if key, err := s.GetKey(fieldName); err == nil {
delim := parseDelim(tpField.Tag.Get("delim")) delim := parseDelim(tpField.Tag.Get("delim"))
if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil { if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil {
return fmt.Errorf("set field %q: %v", fieldName, err) return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
} }
} }
} }
return nil return nil
} }
// mapToSlice maps all sections with the same name and returns the new value. // MapTo maps section to given struct.
// The type of the Value must be a slice. func (s *Section) MapTo(v interface{}) error {
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) typ := reflect.TypeOf(v)
val := reflect.ValueOf(v) val := reflect.ValueOf(v)
if typ.Kind() == reflect.Ptr { if typ.Kind() == reflect.Ptr {
typ = typ.Elem() typ = typ.Elem()
val = val.Elem() val = val.Elem()
} else { } else {
return errors.New("not a pointer to a struct") return errors.New("cannot map to non-pointer struct")
} }
if typ.Kind() == reflect.Slice { return s.mapTo(val, false)
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, // StrictMapTo maps section to given struct in strict mode,
// which returns all possible error including value parsing error. // which returns all possible error including value parsing error.
func (s *Section) StrictMapTo(v interface{}) error { func (s *Section) StrictMapTo(v interface{}) error {
return s.mapTo(v, true) typ := reflect.TypeOf(v)
val := reflect.ValueOf(v)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
} else {
return errors.New("cannot map to non-pointer struct")
}
return s.mapTo(val, true)
} }
// MapTo maps file to given struct. // MapTo maps file to given struct.
@ -476,8 +369,6 @@ func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, all
val = fmt.Sprint(slice.Index(i).Uint()) val = fmt.Sprint(slice.Index(i).Uint())
case reflect.Float64: case reflect.Float64:
val = fmt.Sprint(slice.Index(i).Float()) val = fmt.Sprint(slice.Index(i).Float())
case reflect.Bool:
val = fmt.Sprint(slice.Index(i).Bool())
case reflectTime: case reflectTime:
val = slice.Index(i).Interface().(time.Time).Format(time.RFC3339) val = slice.Index(i).Interface().(time.Time).Format(time.RFC3339)
default: default:
@ -487,10 +378,10 @@ func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, all
if i == 0 { if i == 0 {
keyWithShadows = newKey(key.s, key.name, val) keyWithShadows = newKey(key.s, key.name, val)
} else { } else {
_ = keyWithShadows.AddShadow(val) keyWithShadows.AddShadow(val)
} }
} }
*key = *keyWithShadows key = keyWithShadows
return nil return nil
} }
@ -505,8 +396,6 @@ func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, all
buf.WriteString(fmt.Sprint(slice.Index(i).Uint())) buf.WriteString(fmt.Sprint(slice.Index(i).Uint()))
case reflect.Float64: case reflect.Float64:
buf.WriteString(fmt.Sprint(slice.Index(i).Float())) buf.WriteString(fmt.Sprint(slice.Index(i).Float()))
case reflect.Bool:
buf.WriteString(fmt.Sprint(slice.Index(i).Bool()))
case reflectTime: case reflectTime:
buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339)) buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339))
default: default:
@ -535,12 +424,8 @@ func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim
key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339))) key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339)))
case reflect.Slice: case reflect.Slice:
return reflectSliceWithProperType(key, field, delim, allowShadow) return reflectSliceWithProperType(key, field, delim, allowShadow)
case reflect.Ptr:
if !field.IsNil() {
return reflectWithProperType(t.Elem(), key, field.Elem(), delim, allowShadow)
}
default: default:
return fmt.Errorf("unsupported type %q", t) return fmt.Errorf("unsupported type '%s'", t)
} }
return nil return nil
} }
@ -568,11 +453,6 @@ func isEmptyValue(v reflect.Value) bool {
return false 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 { func (s *Section) reflectFrom(val reflect.Value) error {
if val.Kind() == reflect.Ptr { if val.Kind() == reflect.Ptr {
val = val.Elem() val = val.Elem()
@ -580,10 +460,6 @@ func (s *Section) reflectFrom(val reflect.Value) error {
typ := val.Type() typ := val.Type()
for i := 0; i < typ.NumField(); i++ { for i := 0; i < typ.NumField(); i++ {
if !val.Field(i).CanInterface() {
continue
}
field := val.Field(i) field := val.Field(i)
tpField := typ.Field(i) tpField := typ.Field(i)
@ -592,28 +468,17 @@ func (s *Section) reflectFrom(val reflect.Value) error {
continue continue
} }
rawName, omitEmpty, allowShadow, allowNonUnique, extends := parseTagOptions(tag) rawName, omitEmpty, allowShadow := parseTagOptions(tag)
if omitEmpty && isEmptyValue(field) { if omitEmpty && isEmptyValue(field) {
continue continue
} }
if r, ok := field.Interface().(StructReflector); ok {
return r.ReflectINIStruct(s.f)
}
fieldName := s.parseFieldName(tpField.Name, rawName) fieldName := s.parseFieldName(tpField.Name, rawName)
if len(fieldName) == 0 || !field.CanSet() { if len(fieldName) == 0 || !field.CanSet() {
continue continue
} }
if extends && tpField.Anonymous && (tpField.Type.Kind() == reflect.Ptr || tpField.Type.Kind() == reflect.Struct) { if (tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous) ||
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") { (tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") {
// Note: The only error here is section doesn't exist. // Note: The only error here is section doesn't exist.
sec, err := s.f.GetSection(fieldName) sec, err := s.f.GetSection(fieldName)
@ -628,41 +493,12 @@ func (s *Section) reflectFrom(val reflect.Value) error {
} }
if err = sec.reflectFrom(field); err != nil { if err = sec.reflectFrom(field); err != nil {
return fmt.Errorf("reflect from field %q: %v", fieldName, err) return fmt.Errorf("error reflecting field (%s): %v", fieldName, err)
} }
continue continue
} }
if allowNonUnique && tpField.Type.Kind() == reflect.Slice { // Note: Same reason as secion.
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) key, err := s.GetKey(fieldName)
if err != nil { if err != nil {
key, _ = s.NewKey(fieldName, "") key, _ = s.NewKey(fieldName, "")
@ -673,58 +509,23 @@ func (s *Section) reflectFrom(val reflect.Value) error {
key.Comment = tpField.Tag.Get("comment") key.Comment = tpField.Tag.Get("comment")
} }
delim := parseDelim(tpField.Tag.Get("delim")) if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim")), allowShadow); err != nil {
if err = reflectWithProperType(tpField.Type, key, field, delim, allowShadow); err != nil { return fmt.Errorf("error reflecting field (%s): %v", fieldName, err)
return fmt.Errorf("reflect field %q: %v", fieldName, err)
} }
} }
return nil return nil
} }
// ReflectFrom reflects section from given struct. It overwrites existing ones. // ReflectFrom reflects secion from given struct.
func (s *Section) ReflectFrom(v interface{}) error { func (s *Section) ReflectFrom(v interface{}) error {
typ := reflect.TypeOf(v) typ := reflect.TypeOf(v)
val := reflect.ValueOf(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 { if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem() val = val.Elem()
} else { } else {
return errors.New("not a pointer to a struct") return errors.New("cannot reflect from non-pointer struct")
} }
return s.reflectFrom(val) return s.reflectFrom(val)

8
vendor/modules.txt vendored
View File

@ -1,13 +1,11 @@
# github.com/davecgh/go-spew v1.1.1 # github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc
## explicit
# github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
## explicit ## explicit
github.com/influxdata/influxdb1-client github.com/influxdata/influxdb1-client
github.com/influxdata/influxdb1-client/models github.com/influxdata/influxdb1-client/models
github.com/influxdata/influxdb1-client/pkg/escape github.com/influxdata/influxdb1-client/pkg/escape
github.com/influxdata/influxdb1-client/v2 github.com/influxdata/influxdb1-client/v2
# github.com/stretchr/testify v1.3.0 # github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945
## explicit ## explicit
# gopkg.in/ini.v1 v1.67.0 # gopkg.in/ini.v1 v1.44.0
## explicit ## explicit
gopkg.in/ini.v1 gopkg.in/ini.v1