Codebase list golang-github-spf13-viper / upstream/1.10.1+git20220312.1.dc76f3c
Import upstream version 1.10.1+git20220312.1.dc76f3c Debian Janitor 2 years ago
41 changed file(s) with 1862 addition(s) and 276 deletion(s). Raw diff Collapse all Expand all
1717 - label: I have searched the [issue tracker](https://www.github.com/spf13/viper/issues) for an issue that matches the one I want to file, without success.
1818 required: true
1919 - label: I am not looking for support or already pursued the available [support channels](https://github.com/spf13/viper/issues/new/choose) without success.
20 required: true
21 - label: I have checked the [troubleshooting guide](https://github.com/spf13/viper/blob/master/TROUBLESHOOTING.md) for my problem, without success.
2022 required: true
2123 - type: input
2224 attributes:
1616 matrix:
1717 os: [ubuntu-latest, macos-latest, windows-latest]
1818 go: ['1.14', '1.15', '1.16', '1.17']
19 tags: ['', 'viper_yaml3', 'viper_toml2']
1920 env:
2021 GOFLAGS: -mod=readonly
2122
2627 go-version: ${{ matrix.go }}
2728
2829 - name: Checkout code
29 uses: actions/checkout@v2
30 uses: actions/checkout@v3
3031
3132 - name: Test
32 run: go test -race -v ./...
33 run: go test -race -tags '${{ matrix.tags }}' -v ./...
34 if: runner.os != 'Windows'
35
36 - name: Test (without race detector)
37 run: go test -tags '${{ matrix.tags }}' -v ./...
38 if: runner.os == 'Windows'
3339
3440 lint:
3541 name: Lint
4450 go-version: 1.17
4551
4652 - name: Checkout code
47 uses: actions/checkout@v2
53 uses: actions/checkout@v3
4854
4955 - name: Lint
5056 uses: golangci/golangci-lint-action@v2
3838
3939 steps:
4040 - name: Checkout repository
41 uses: actions/checkout@v2
41 uses: actions/checkout@v3
4242
4343 # Initializes the CodeQL tools for scanning.
4444 - name: Initialize CodeQL
55 comment:
66 runs-on: ubuntu-latest
77 steps:
8 - uses: actions/github-script@v5
8 - uses: actions/github-script@v6
99 with:
1010 github-token: ${{secrets.GITHUB_TOKEN}}
1111 script: |
55 comment:
66 runs-on: ubuntu-latest
77 steps:
8 - uses: actions/github-script@v5
8 - uses: actions/github-script@v6
99 with:
1010 github-token: ${{secrets.GITHUB_TOKEN}}
1111 script: |
1919 go-version: '1.17'
2020
2121 - name: Checkout code
22 uses: actions/checkout@v2
22 uses: actions/checkout@v3
2323
2424 - name: Ensure Viper compiles for WASM
2525 run: GOOS=js GOARCH=wasm go build .
2020 Please refer to the [wiki](https://github.com/golang/go/wiki/Modules) on how to do that.
2121
2222 **tl;dr* `export GO111MODULE=on`
23
24 ## Unquoted 'y' and 'n' characters get replaced with _true_ and _false_ when reading a YAML file
25
26 This is a YAML 1.1 feature according to [go-yaml/yaml#740](https://github.com/go-yaml/yaml/issues/740).
27
28 Potential solutions are:
29
30 1. Quoting values resolved as boolean
31 1. Upgrading to YAML v3 (for the time being this is possible by passing the `viper_yaml3` tag to your build)
0 //go:build viper_logger
1 // +build viper_logger
2
3 package viper
4
5 // WithLogger sets a custom logger.
6 func WithLogger(l Logger) Option {
7 return optionFunc(func(v *Viper) {
8 v.logger = l
9 })
10 }
44 require (
55 github.com/fsnotify/fsnotify v1.5.1
66 github.com/hashicorp/hcl v1.0.0
7 github.com/magiconair/properties v1.8.5
7 github.com/magiconair/properties v1.8.6
88 github.com/mitchellh/mapstructure v1.4.3
99 github.com/pelletier/go-toml v1.9.4
10 github.com/pelletier/go-toml/v2 v2.0.0-beta.6
1011 github.com/sagikazarmark/crypt v0.4.0
11 github.com/spf13/afero v1.6.0
12 github.com/spf13/afero v1.8.2
1213 github.com/spf13/cast v1.4.1
1314 github.com/spf13/jwalterweatherman v1.1.0
1415 github.com/spf13/pflag v1.0.5
15 github.com/stretchr/testify v1.7.0
16 github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942
1617 github.com/subosito/gotenv v1.2.0
17 gopkg.in/ini.v1 v1.66.2
18 gopkg.in/ini.v1 v1.66.4
1819 gopkg.in/yaml.v2 v2.4.0
20 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
1921 )
2022
2123 require (
5355 go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
5456 go.etcd.io/etcd/client/v2 v2.305.1 // indirect
5557 go.opencensus.io v0.23.0 // indirect
56 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
58 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect
5759 golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
5860 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
5961 golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
6466 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
6567 google.golang.org/grpc v1.43.0 // indirect
6668 google.golang.org/protobuf v1.27.1 // indirect
67 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
6869 )
22 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
33 cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
44 cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
5 cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
56 cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
67 cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
78 cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
1415 cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
1516 cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
1617 cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
18 cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
1719 cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
1820 cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
1921 cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
4547 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
4648 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
4749 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
50 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
4851 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
4952 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
5053 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
192195 github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
193196 github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
194197 github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
198 github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
195199 github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
196200 github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
197201 github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
205209 github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
206210 github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
207211 github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
212 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
208213 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
209214 github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY=
210215 github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
271276 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
272277 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
273278 github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
274 github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
275 github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
279 github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
280 github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
276281 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
277282 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
278283 github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
312317 github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
313318 github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
314319 github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
320 github.com/pelletier/go-toml/v2 v2.0.0-beta.6 h1:JFNqj2afbbhCqTiyN16D7Tudc/aaDzE2FBDk+VlBQnE=
321 github.com/pelletier/go-toml/v2 v2.0.0-beta.6/go.mod h1:ke6xncR3W76Ba8xnVxkrZG0js6Rd2BsQEAYrfgJ6eQA=
315322 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
316 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
317323 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
324 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
325 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
318326 github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
327 github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
319328 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
320329 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
321330 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
343352 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
344353 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
345354 github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
346 github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
347355 github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
356 github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
357 github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
348358 github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
349359 github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
350360 github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
359369 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
360370 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
361371 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
362 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
363372 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
373 github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942 h1:t0lM6y/M5IiUZyvbBTcngso8SZEZICH7is9B6g/obVU=
374 github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
364375 github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
365376 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
366377 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
395406 golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
396407 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
397408 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
398 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
409 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
399410 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
411 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4=
412 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
400413 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
401414 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
402415 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
466479 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
467480 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
468481 golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
482 golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
469483 golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
470484 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
471485 golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
546560 golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
547561 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
548562 golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
563 golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
549564 golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
550565 golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
551566 golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
553568 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
554569 golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
555570 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
571 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
556572 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
557573 golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
558574 golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
630646 golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
631647 golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
632648 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
649 golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
633650 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
634651 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
635652 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
717734 google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
718735 google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
719736 google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
737 google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
720738 google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
739 google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
721740 google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
722741 google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
723742 google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
793812 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
794813 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
795814 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
796 gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
797 gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
815 gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
816 gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
798817 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
799818 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
800819 gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
33 "sync"
44 )
55
6 // Decoder decodes the contents of b into a v representation.
6 // Decoder decodes the contents of b into v.
77 // It's primarily used for decoding contents of a file into a map[string]interface{}.
88 type Decoder interface {
9 Decode(b []byte, v interface{}) error
9 Decode(b []byte, v map[string]interface{}) error
1010 }
1111
1212 const (
4747 }
4848
4949 // Decode calls the underlying Decoder based on the format.
50 func (e *DecoderRegistry) Decode(format string, b []byte, v interface{}) error {
50 func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]interface{}) error {
5151 e.mu.RLock()
5252 decoder, ok := e.decoders[format]
5353 e.mu.RUnlock()
00 package encoding
11
22 import (
3 "reflect"
34 "testing"
45 )
56
67 type decoder struct {
7 v interface{}
8 v map[string]interface{}
89 }
910
10 func (d decoder) Decode(_ []byte, v interface{}) error {
11 rv := v.(*string)
12 *rv = d.v.(string)
11 func (d decoder) Decode(_ []byte, v map[string]interface{}) error {
12 for key, value := range d.v {
13 v[key] = value
14 }
1315
1416 return nil
1517 }
4345 t.Run("OK", func(t *testing.T) {
4446 registry := NewDecoderRegistry()
4547 decoder := decoder{
46 v: "decoded value",
48 v: map[string]interface{}{
49 "key": "value",
50 },
4751 }
4852
4953 err := registry.RegisterDecoder("myformat", decoder)
5155 t.Fatal(err)
5256 }
5357
54 var v string
58 v := map[string]interface{}{}
5559
56 err = registry.Decode("myformat", []byte("some value"), &v)
60 err = registry.Decode("myformat", []byte("key: value"), v)
5761 if err != nil {
5862 t.Fatal(err)
5963 }
6064
61 if v != "decoded value" {
62 t.Fatalf("expected 'decoded value', got: %#v", v)
65 if !reflect.DeepEqual(decoder.v, v) {
66 t.Fatalf("decoded value does not match the expected one\nactual: %+v\nexpected: %+v", v, decoder.v)
6367 }
6468 })
6569
6670 t.Run("DecoderNotFound", func(t *testing.T) {
6771 registry := NewDecoderRegistry()
6872
69 var v string
73 v := map[string]interface{}{}
7074
71 err := registry.Decode("myformat", []byte("some value"), &v)
75 err := registry.Decode("myformat", nil, v)
7276 if err != ErrDecoderNotFound {
7377 t.Fatalf("expected ErrDecoderNotFound, got: %v", err)
7478 }
0 package dotenv
1
2 import (
3 "bytes"
4 "fmt"
5 "sort"
6 "strings"
7
8 "github.com/subosito/gotenv"
9 )
10
11 const keyDelimiter = "_"
12
13 // Codec implements the encoding.Encoder and encoding.Decoder interfaces for encoding data containing environment variables
14 // (commonly called as dotenv format).
15 type Codec struct{}
16
17 func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
18 flattened := map[string]interface{}{}
19
20 flattened = flattenAndMergeMap(flattened, v, "", keyDelimiter)
21
22 keys := make([]string, 0, len(flattened))
23
24 for key := range flattened {
25 keys = append(keys, key)
26 }
27
28 sort.Strings(keys)
29
30 var buf bytes.Buffer
31
32 for _, key := range keys {
33 _, err := buf.WriteString(fmt.Sprintf("%v=%v\n", strings.ToUpper(key), flattened[key]))
34 if err != nil {
35 return nil, err
36 }
37 }
38
39 return buf.Bytes(), nil
40 }
41
42 func (Codec) Decode(b []byte, v map[string]interface{}) error {
43 var buf bytes.Buffer
44
45 _, err := buf.Write(b)
46 if err != nil {
47 return err
48 }
49
50 env, err := gotenv.StrictParse(&buf)
51 if err != nil {
52 return err
53 }
54
55 for key, value := range env {
56 v[key] = value
57 }
58
59 return nil
60 }
0 package dotenv
1
2 import (
3 "reflect"
4 "testing"
5 )
6
7 // original form of the data
8 const original = `# key-value pair
9 KEY=value
10 `
11
12 // encoded form of the data
13 const encoded = `KEY=value
14 `
15
16 // Viper's internal representation
17 var data = map[string]interface{}{
18 "KEY": "value",
19 }
20
21 func TestCodec_Encode(t *testing.T) {
22 codec := Codec{}
23
24 b, err := codec.Encode(data)
25 if err != nil {
26 t.Fatal(err)
27 }
28
29 if encoded != string(b) {
30 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
31 }
32 }
33
34 func TestCodec_Decode(t *testing.T) {
35 t.Run("OK", func(t *testing.T) {
36 codec := Codec{}
37
38 v := map[string]interface{}{}
39
40 err := codec.Decode([]byte(original), v)
41 if err != nil {
42 t.Fatal(err)
43 }
44
45 if !reflect.DeepEqual(data, v) {
46 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
47 }
48 })
49
50 t.Run("InvalidData", func(t *testing.T) {
51 codec := Codec{}
52
53 v := map[string]interface{}{}
54
55 err := codec.Decode([]byte(`invalid data`), v)
56 if err == nil {
57 t.Fatal("expected decoding to fail")
58 }
59
60 t.Logf("decoding failed as expected: %s", err)
61 })
62 }
0 package dotenv
1
2 import (
3 "strings"
4
5 "github.com/spf13/cast"
6 )
7
8 // flattenAndMergeMap recursively flattens the given map into a new map
9 // Code is based on the function with the same name in tha main package.
10 // TODO: move it to a common place
11 func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
12 if shadow != nil && prefix != "" && shadow[prefix] != nil {
13 // prefix is shadowed => nothing more to flatten
14 return shadow
15 }
16 if shadow == nil {
17 shadow = make(map[string]interface{})
18 }
19
20 var m2 map[string]interface{}
21 if prefix != "" {
22 prefix += delimiter
23 }
24 for k, val := range m {
25 fullKey := prefix + k
26 switch val.(type) {
27 case map[string]interface{}:
28 m2 = val.(map[string]interface{})
29 case map[interface{}]interface{}:
30 m2 = cast.ToStringMap(val)
31 default:
32 // immediate value
33 shadow[strings.ToLower(fullKey)] = val
34 continue
35 }
36 // recursively merge to shadow map
37 shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
38 }
39 return shadow
40 }
66 // Encoder encodes the contents of v into a byte representation.
77 // It's primarily used for encoding a map[string]interface{} into a file format.
88 type Encoder interface {
9 Encode(v interface{}) ([]byte, error)
9 Encode(v map[string]interface{}) ([]byte, error)
1010 }
1111
1212 const (
4646 return nil
4747 }
4848
49 func (e *EncoderRegistry) Encode(format string, v interface{}) ([]byte, error) {
49 func (e *EncoderRegistry) Encode(format string, v map[string]interface{}) ([]byte, error) {
5050 e.mu.RLock()
5151 encoder, ok := e.encoders[format]
5252 e.mu.RUnlock()
77 b []byte
88 }
99
10 func (e encoder) Encode(_ interface{}) ([]byte, error) {
10 func (e encoder) Encode(_ map[string]interface{}) ([]byte, error) {
1111 return e.b, nil
1212 }
1313
4040 t.Run("OK", func(t *testing.T) {
4141 registry := NewEncoderRegistry()
4242 encoder := encoder{
43 b: []byte("encoded value"),
43 b: []byte("key: value"),
4444 }
4545
4646 err := registry.RegisterEncoder("myformat", encoder)
4848 t.Fatal(err)
4949 }
5050
51 b, err := registry.Encode("myformat", "some value")
51 b, err := registry.Encode("myformat", map[string]interface{}{"key": "value"})
5252 if err != nil {
5353 t.Fatal(err)
5454 }
5555
56 if string(b) != "encoded value" {
57 t.Fatalf("expected 'encoded value', got: %#v", string(b))
56 if string(b) != "key: value" {
57 t.Fatalf("expected 'key: value', got: %#v", string(b))
5858 }
5959 })
6060
6161 t.Run("EncoderNotFound", func(t *testing.T) {
6262 registry := NewEncoderRegistry()
6363
64 _, err := registry.Encode("myformat", "some value")
64 _, err := registry.Encode("myformat", map[string]interface{}{"key": "value"})
6565 if err != ErrEncoderNotFound {
6666 t.Fatalf("expected ErrEncoderNotFound, got: %v", err)
6767 }
1111 // TODO: add printer config to the codec?
1212 type Codec struct{}
1313
14 func (Codec) Encode(v interface{}) ([]byte, error) {
14 func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
1515 b, err := json.Marshal(v)
1616 if err != nil {
1717 return nil, err
3434 return buf.Bytes(), nil
3535 }
3636
37 func (Codec) Decode(b []byte, v interface{}) error {
38 return hcl.Unmarshal(b, v)
37 func (Codec) Decode(b []byte, v map[string]interface{}) error {
38 return hcl.Unmarshal(b, &v)
3939 }
0 package hcl
1
2 import (
3 "reflect"
4 "testing"
5 )
6
7 // original form of the data
8 const original = `# key-value pair
9 "key" = "value"
10
11 // list
12 "list" = ["item1", "item2", "item3"]
13
14 /* map */
15 "map" = {
16 "key" = "value"
17 }
18
19 /*
20 nested map
21 */
22 "nested_map" "map" {
23 "key" = "value"
24
25 "list" = ["item1", "item2", "item3"]
26 }`
27
28 // encoded form of the data
29 const encoded = `"key" = "value"
30
31 "list" = ["item1", "item2", "item3"]
32
33 "map" = {
34 "key" = "value"
35 }
36
37 "nested_map" "map" {
38 "key" = "value"
39
40 "list" = ["item1", "item2", "item3"]
41 }`
42
43 // decoded form of the data
44 //
45 // in case of HCL it's slightly different from Viper's internal representation
46 // (eg. map is decoded into a list of maps)
47 var decoded = map[string]interface{}{
48 "key": "value",
49 "list": []interface{}{
50 "item1",
51 "item2",
52 "item3",
53 },
54 "map": []map[string]interface{}{
55 {
56 "key": "value",
57 },
58 },
59 "nested_map": []map[string]interface{}{
60 {
61 "map": []map[string]interface{}{
62 {
63 "key": "value",
64 "list": []interface{}{
65 "item1",
66 "item2",
67 "item3",
68 },
69 },
70 },
71 },
72 },
73 }
74
75 // Viper's internal representation
76 var data = map[string]interface{}{
77 "key": "value",
78 "list": []interface{}{
79 "item1",
80 "item2",
81 "item3",
82 },
83 "map": map[string]interface{}{
84 "key": "value",
85 },
86 "nested_map": map[string]interface{}{
87 "map": map[string]interface{}{
88 "key": "value",
89 "list": []interface{}{
90 "item1",
91 "item2",
92 "item3",
93 },
94 },
95 },
96 }
97
98 func TestCodec_Encode(t *testing.T) {
99 codec := Codec{}
100
101 b, err := codec.Encode(data)
102 if err != nil {
103 t.Fatal(err)
104 }
105
106 if encoded != string(b) {
107 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
108 }
109 }
110
111 func TestCodec_Decode(t *testing.T) {
112 t.Run("OK", func(t *testing.T) {
113 codec := Codec{}
114
115 v := map[string]interface{}{}
116
117 err := codec.Decode([]byte(original), v)
118 if err != nil {
119 t.Fatal(err)
120 }
121
122 if !reflect.DeepEqual(decoded, v) {
123 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
124 }
125 })
126
127 t.Run("InvalidData", func(t *testing.T) {
128 codec := Codec{}
129
130 v := map[string]interface{}{}
131
132 err := codec.Decode([]byte(`invalid data`), v)
133 if err == nil {
134 t.Fatal("expected decoding to fail")
135 }
136
137 t.Logf("decoding failed as expected: %s", err)
138 })
139 }
0 package ini
1
2 import (
3 "bytes"
4 "sort"
5 "strings"
6
7 "github.com/spf13/cast"
8 "gopkg.in/ini.v1"
9 )
10
11 // LoadOptions contains all customized options used for load data source(s).
12 // This type is added here for convenience: this way consumers can import a single package called "ini".
13 type LoadOptions = ini.LoadOptions
14
15 // Codec implements the encoding.Encoder and encoding.Decoder interfaces for INI encoding.
16 type Codec struct {
17 KeyDelimiter string
18 LoadOptions LoadOptions
19 }
20
21 func (c Codec) Encode(v map[string]interface{}) ([]byte, error) {
22 cfg := ini.Empty()
23 ini.PrettyFormat = false
24
25 flattened := map[string]interface{}{}
26
27 flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
28
29 keys := make([]string, 0, len(flattened))
30
31 for key := range flattened {
32 keys = append(keys, key)
33 }
34
35 sort.Strings(keys)
36
37 for _, key := range keys {
38 sectionName, keyName := "", key
39
40 lastSep := strings.LastIndex(key, ".")
41 if lastSep != -1 {
42 sectionName = key[:(lastSep)]
43 keyName = key[(lastSep + 1):]
44 }
45
46 // TODO: is this a good idea?
47 if sectionName == "default" {
48 sectionName = ""
49 }
50
51 cfg.Section(sectionName).Key(keyName).SetValue(cast.ToString(flattened[key]))
52 }
53
54 var buf bytes.Buffer
55
56 _, err := cfg.WriteTo(&buf)
57 if err != nil {
58 return nil, err
59 }
60
61 return buf.Bytes(), nil
62 }
63
64 func (c Codec) Decode(b []byte, v map[string]interface{}) error {
65 cfg := ini.Empty(c.LoadOptions)
66
67 err := cfg.Append(b)
68 if err != nil {
69 return err
70 }
71
72 sections := cfg.Sections()
73
74 for i := 0; i < len(sections); i++ {
75 section := sections[i]
76 keys := section.Keys()
77
78 for j := 0; j < len(keys); j++ {
79 key := keys[j]
80 value := cfg.Section(section.Name()).Key(key.Name()).String()
81
82 deepestMap := deepSearch(v, strings.Split(section.Name(), c.keyDelimiter()))
83
84 // set innermost value
85 deepestMap[key.Name()] = value
86 }
87 }
88
89 return nil
90 }
91
92 func (c Codec) keyDelimiter() string {
93 if c.KeyDelimiter == "" {
94 return "."
95 }
96
97 return c.KeyDelimiter
98 }
0 package ini
1
2 import (
3 "reflect"
4 "testing"
5 )
6
7 // original form of the data
8 const original = `; key-value pair
9 key=value ; key-value pair
10
11 # map
12 [map] # map
13 key=%(key)s
14
15 `
16
17 // encoded form of the data
18 const encoded = `key=value
19
20 [map]
21 key=value
22
23 `
24
25 // decoded form of the data
26 //
27 // in case of INI it's slightly different from Viper's internal representation
28 // (eg. top level keys land in a section called default)
29 var decoded = map[string]interface{}{
30 "DEFAULT": map[string]interface{}{
31 "key": "value",
32 },
33 "map": map[string]interface{}{
34 "key": "value",
35 },
36 }
37
38 // Viper's internal representation
39 var data = map[string]interface{}{
40 "key": "value",
41 "map": map[string]interface{}{
42 "key": "value",
43 },
44 }
45
46 func TestCodec_Encode(t *testing.T) {
47 t.Run("OK", func(t *testing.T) {
48 codec := Codec{}
49
50 b, err := codec.Encode(data)
51 if err != nil {
52 t.Fatal(err)
53 }
54
55 if encoded != string(b) {
56 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
57 }
58 })
59
60 t.Run("Default", func(t *testing.T) {
61 codec := Codec{}
62
63 data := map[string]interface{}{
64 "default": map[string]interface{}{
65 "key": "value",
66 },
67 "map": map[string]interface{}{
68 "key": "value",
69 },
70 }
71
72 b, err := codec.Encode(data)
73 if err != nil {
74 t.Fatal(err)
75 }
76
77 if encoded != string(b) {
78 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
79 }
80 })
81 }
82
83 func TestCodec_Decode(t *testing.T) {
84 t.Run("OK", func(t *testing.T) {
85 codec := Codec{}
86
87 v := map[string]interface{}{}
88
89 err := codec.Decode([]byte(original), v)
90 if err != nil {
91 t.Fatal(err)
92 }
93
94 if !reflect.DeepEqual(decoded, v) {
95 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
96 }
97 })
98
99 t.Run("InvalidData", func(t *testing.T) {
100 codec := Codec{}
101
102 v := map[string]interface{}{}
103
104 err := codec.Decode([]byte(`invalid data`), v)
105 if err == nil {
106 t.Fatal("expected decoding to fail")
107 }
108
109 t.Logf("decoding failed as expected: %s", err)
110 })
111 }
0 package ini
1
2 import (
3 "strings"
4
5 "github.com/spf13/cast"
6 )
7
8 // THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
9 // AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE
10 // deepSearch scans deep maps, following the key indexes listed in the
11 // sequence "path".
12 // The last value is expected to be another map, and is returned.
13 //
14 // In case intermediate keys do not exist, or map to a non-map value,
15 // a new map is created and inserted, and the search continues from there:
16 // the initial map "m" may be modified!
17 func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
18 for _, k := range path {
19 m2, ok := m[k]
20 if !ok {
21 // intermediate key does not exist
22 // => create it and continue from there
23 m3 := make(map[string]interface{})
24 m[k] = m3
25 m = m3
26 continue
27 }
28 m3, ok := m2.(map[string]interface{})
29 if !ok {
30 // intermediate key is a value
31 // => replace with a new map
32 m3 = make(map[string]interface{})
33 m[k] = m3
34 }
35 // continue search from here
36 m = m3
37 }
38 return m
39 }
40
41 // flattenAndMergeMap recursively flattens the given map into a new map
42 // Code is based on the function with the same name in tha main package.
43 // TODO: move it to a common place
44 func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
45 if shadow != nil && prefix != "" && shadow[prefix] != nil {
46 // prefix is shadowed => nothing more to flatten
47 return shadow
48 }
49 if shadow == nil {
50 shadow = make(map[string]interface{})
51 }
52
53 var m2 map[string]interface{}
54 if prefix != "" {
55 prefix += delimiter
56 }
57 for k, val := range m {
58 fullKey := prefix + k
59 switch val.(type) {
60 case map[string]interface{}:
61 m2 = val.(map[string]interface{})
62 case map[interface{}]interface{}:
63 m2 = cast.ToStringMap(val)
64 default:
65 // immediate value
66 shadow[strings.ToLower(fullKey)] = val
67 continue
68 }
69 // recursively merge to shadow map
70 shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
71 }
72 return shadow
73 }
0 package javaproperties
1
2 import (
3 "bytes"
4 "sort"
5 "strings"
6
7 "github.com/magiconair/properties"
8 "github.com/spf13/cast"
9 )
10
11 // Codec implements the encoding.Encoder and encoding.Decoder interfaces for Java properties encoding.
12 type Codec struct {
13 KeyDelimiter string
14
15 // Store read properties on the object so that we can write back in order with comments.
16 // This will only be used if the configuration read is a properties file.
17 // TODO: drop this feature in v2
18 // TODO: make use of the global properties object optional
19 Properties *properties.Properties
20 }
21
22 func (c *Codec) Encode(v map[string]interface{}) ([]byte, error) {
23 if c.Properties == nil {
24 c.Properties = properties.NewProperties()
25 }
26
27 flattened := map[string]interface{}{}
28
29 flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
30
31 keys := make([]string, 0, len(flattened))
32
33 for key := range flattened {
34 keys = append(keys, key)
35 }
36
37 sort.Strings(keys)
38
39 for _, key := range keys {
40 _, _, err := c.Properties.Set(key, cast.ToString(flattened[key]))
41 if err != nil {
42 return nil, err
43 }
44 }
45
46 var buf bytes.Buffer
47
48 _, err := c.Properties.WriteComment(&buf, "#", properties.UTF8)
49 if err != nil {
50 return nil, err
51 }
52
53 return buf.Bytes(), nil
54 }
55
56 func (c *Codec) Decode(b []byte, v map[string]interface{}) error {
57 var err error
58 c.Properties, err = properties.Load(b, properties.UTF8)
59 if err != nil {
60 return err
61 }
62
63 for _, key := range c.Properties.Keys() {
64 // ignore existence check: we know it's there
65 value, _ := c.Properties.Get(key)
66
67 // recursively build nested maps
68 path := strings.Split(key, c.keyDelimiter())
69 lastKey := strings.ToLower(path[len(path)-1])
70 deepestMap := deepSearch(v, path[0:len(path)-1])
71
72 // set innermost value
73 deepestMap[lastKey] = value
74 }
75
76 return nil
77 }
78
79 func (c Codec) keyDelimiter() string {
80 if c.KeyDelimiter == "" {
81 return "."
82 }
83
84 return c.KeyDelimiter
85 }
0 package javaproperties
1
2 import (
3 "reflect"
4 "testing"
5 )
6
7 // original form of the data
8 const original = `#key-value pair
9 key = value
10 map.key = value
11 `
12
13 // encoded form of the data
14 const encoded = `key = value
15 map.key = value
16 `
17
18 // Viper's internal representation
19 var data = map[string]interface{}{
20 "key": "value",
21 "map": map[string]interface{}{
22 "key": "value",
23 },
24 }
25
26 func TestCodec_Encode(t *testing.T) {
27 codec := Codec{}
28
29 b, err := codec.Encode(data)
30 if err != nil {
31 t.Fatal(err)
32 }
33
34 if encoded != string(b) {
35 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
36 }
37 }
38
39 func TestCodec_Decode(t *testing.T) {
40 t.Run("OK", func(t *testing.T) {
41 codec := Codec{}
42
43 v := map[string]interface{}{}
44
45 err := codec.Decode([]byte(original), v)
46 if err != nil {
47 t.Fatal(err)
48 }
49
50 if !reflect.DeepEqual(data, v) {
51 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
52 }
53 })
54
55 t.Run("InvalidData", func(t *testing.T) {
56 t.Skip("TODO: needs invalid data example")
57
58 codec := Codec{}
59
60 v := map[string]interface{}{}
61
62 codec.Decode([]byte(``), v)
63
64 if len(v) > 0 {
65 t.Fatalf("expected map to be empty when data is invalid\nactual: %#v", v)
66 }
67 })
68 }
69
70 func TestCodec_DecodeEncode(t *testing.T) {
71 codec := Codec{}
72
73 v := map[string]interface{}{}
74
75 err := codec.Decode([]byte(original), v)
76 if err != nil {
77 t.Fatal(err)
78 }
79
80 b, err := codec.Encode(data)
81 if err != nil {
82 t.Fatal(err)
83 }
84
85 if original != string(b) {
86 t.Fatalf("encoded value does not match the original\nactual: %#v\nexpected: %#v", string(b), original)
87 }
88 }
0 package javaproperties
1
2 import (
3 "strings"
4
5 "github.com/spf13/cast"
6 )
7
8 // THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
9 // AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE
10 // deepSearch scans deep maps, following the key indexes listed in the
11 // sequence "path".
12 // The last value is expected to be another map, and is returned.
13 //
14 // In case intermediate keys do not exist, or map to a non-map value,
15 // a new map is created and inserted, and the search continues from there:
16 // the initial map "m" may be modified!
17 func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
18 for _, k := range path {
19 m2, ok := m[k]
20 if !ok {
21 // intermediate key does not exist
22 // => create it and continue from there
23 m3 := make(map[string]interface{})
24 m[k] = m3
25 m = m3
26 continue
27 }
28 m3, ok := m2.(map[string]interface{})
29 if !ok {
30 // intermediate key is a value
31 // => replace with a new map
32 m3 = make(map[string]interface{})
33 m[k] = m3
34 }
35 // continue search from here
36 m = m3
37 }
38 return m
39 }
40
41 // flattenAndMergeMap recursively flattens the given map into a new map
42 // Code is based on the function with the same name in tha main package.
43 // TODO: move it to a common place
44 func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
45 if shadow != nil && prefix != "" && shadow[prefix] != nil {
46 // prefix is shadowed => nothing more to flatten
47 return shadow
48 }
49 if shadow == nil {
50 shadow = make(map[string]interface{})
51 }
52
53 var m2 map[string]interface{}
54 if prefix != "" {
55 prefix += delimiter
56 }
57 for k, val := range m {
58 fullKey := prefix + k
59 switch val.(type) {
60 case map[string]interface{}:
61 m2 = val.(map[string]interface{})
62 case map[interface{}]interface{}:
63 m2 = cast.ToStringMap(val)
64 default:
65 // immediate value
66 shadow[strings.ToLower(fullKey)] = val
67 continue
68 }
69 // recursively merge to shadow map
70 shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
71 }
72 return shadow
73 }
66 // Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding.
77 type Codec struct{}
88
9 func (Codec) Encode(v interface{}) ([]byte, error) {
9 func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
1010 // TODO: expose prefix and indent in the Codec as setting?
1111 return json.MarshalIndent(v, "", " ")
1212 }
1313
14 func (Codec) Decode(b []byte, v interface{}) error {
15 return json.Unmarshal(b, v)
14 func (Codec) Decode(b []byte, v map[string]interface{}) error {
15 return json.Unmarshal(b, &v)
1616 }
0 package json
1
2 import (
3 "reflect"
4 "testing"
5 )
6
7 // encoded form of the data
8 const encoded = `{
9 "key": "value",
10 "list": [
11 "item1",
12 "item2",
13 "item3"
14 ],
15 "map": {
16 "key": "value"
17 },
18 "nested_map": {
19 "map": {
20 "key": "value",
21 "list": [
22 "item1",
23 "item2",
24 "item3"
25 ]
26 }
27 }
28 }`
29
30 // Viper's internal representation
31 var data = map[string]interface{}{
32 "key": "value",
33 "list": []interface{}{
34 "item1",
35 "item2",
36 "item3",
37 },
38 "map": map[string]interface{}{
39 "key": "value",
40 },
41 "nested_map": map[string]interface{}{
42 "map": map[string]interface{}{
43 "key": "value",
44 "list": []interface{}{
45 "item1",
46 "item2",
47 "item3",
48 },
49 },
50 },
51 }
52
53 func TestCodec_Encode(t *testing.T) {
54 codec := Codec{}
55
56 b, err := codec.Encode(data)
57 if err != nil {
58 t.Fatal(err)
59 }
60
61 if encoded != string(b) {
62 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
63 }
64 }
65
66 func TestCodec_Decode(t *testing.T) {
67 t.Run("OK", func(t *testing.T) {
68 codec := Codec{}
69
70 v := map[string]interface{}{}
71
72 err := codec.Decode([]byte(encoded), v)
73 if err != nil {
74 t.Fatal(err)
75 }
76
77 if !reflect.DeepEqual(data, v) {
78 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
79 }
80 })
81
82 t.Run("InvalidData", func(t *testing.T) {
83 codec := Codec{}
84
85 v := map[string]interface{}{}
86
87 err := codec.Decode([]byte(`invalid data`), v)
88 if err == nil {
89 t.Fatal("expected decoding to fail")
90 }
91
92 t.Logf("decoding failed as expected: %s", err)
93 })
94 }
0 //go:build !viper_toml2
1 // +build !viper_toml2
2
03 package toml
14
25 import (
69 // Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
710 type Codec struct{}
811
9 func (Codec) Encode(v interface{}) ([]byte, error) {
10 if m, ok := v.(map[string]interface{}); ok {
11 t, err := toml.TreeFromMap(m)
12 if err != nil {
13 return nil, err
14 }
15
16 s, err := t.ToTomlString()
17 if err != nil {
18 return nil, err
19 }
20
21 return []byte(s), nil
12 func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
13 t, err := toml.TreeFromMap(v)
14 if err != nil {
15 return nil, err
2216 }
2317
24 return toml.Marshal(v)
18 s, err := t.ToTomlString()
19 if err != nil {
20 return nil, err
21 }
22
23 return []byte(s), nil
2524 }
2625
27 func (Codec) Decode(b []byte, v interface{}) error {
26 func (Codec) Decode(b []byte, v map[string]interface{}) error {
2827 tree, err := toml.LoadBytes(b)
2928 if err != nil {
3029 return err
3130 }
3231
33 if m, ok := v.(*map[string]interface{}); ok {
34 vmap := *m
35 tmap := tree.ToMap()
36 for k, v := range tmap {
37 vmap[k] = v
38 }
39
40 return nil
32 tmap := tree.ToMap()
33 for key, value := range tmap {
34 v[key] = value
4135 }
4236
43 return tree.Unmarshal(v)
37 return nil
4438 }
0 //go:build viper_toml2
1 // +build viper_toml2
2
3 package toml
4
5 import (
6 "github.com/pelletier/go-toml/v2"
7 )
8
9 // Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
10 type Codec struct{}
11
12 func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
13 return toml.Marshal(v)
14 }
15
16 func (Codec) Decode(b []byte, v map[string]interface{}) error {
17 return toml.Unmarshal(b, &v)
18 }
0 //go:build viper_toml2
1 // +build viper_toml2
2
3 package toml
4
5 import (
6 "reflect"
7 "testing"
8 )
9
10 // original form of the data
11 const original = `# key-value pair
12 key = "value"
13 list = ["item1", "item2", "item3"]
14
15 [map]
16 key = "value"
17
18 # nested
19 # map
20 [nested_map]
21 [nested_map.map]
22 key = "value"
23 list = [
24 "item1",
25 "item2",
26 "item3",
27 ]
28 `
29
30 // encoded form of the data
31 const encoded = `key = 'value'
32 list = ['item1', 'item2', 'item3']
33 [map]
34 key = 'value'
35
36 [nested_map]
37 [nested_map.map]
38 key = 'value'
39 list = ['item1', 'item2', 'item3']
40
41
42 `
43
44 // Viper's internal representation
45 var data = map[string]interface{}{
46 "key": "value",
47 "list": []interface{}{
48 "item1",
49 "item2",
50 "item3",
51 },
52 "map": map[string]interface{}{
53 "key": "value",
54 },
55 "nested_map": map[string]interface{}{
56 "map": map[string]interface{}{
57 "key": "value",
58 "list": []interface{}{
59 "item1",
60 "item2",
61 "item3",
62 },
63 },
64 },
65 }
66
67 func TestCodec_Encode(t *testing.T) {
68 codec := Codec{}
69
70 b, err := codec.Encode(data)
71 if err != nil {
72 t.Fatal(err)
73 }
74
75 if encoded != string(b) {
76 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
77 }
78 }
79
80 func TestCodec_Decode(t *testing.T) {
81 t.Run("OK", func(t *testing.T) {
82 codec := Codec{}
83
84 v := map[string]interface{}{}
85
86 err := codec.Decode([]byte(original), v)
87 if err != nil {
88 t.Fatal(err)
89 }
90
91 if !reflect.DeepEqual(data, v) {
92 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
93 }
94 })
95
96 t.Run("InvalidData", func(t *testing.T) {
97 codec := Codec{}
98
99 v := map[string]interface{}{}
100
101 err := codec.Decode([]byte(`invalid data`), v)
102 if err == nil {
103 t.Fatal("expected decoding to fail")
104 }
105
106 t.Logf("decoding failed as expected: %s", err)
107 })
108 }
0 //go:build !viper_toml2
1 // +build !viper_toml2
2
3 package toml
4
5 import (
6 "reflect"
7 "testing"
8 )
9
10 // original form of the data
11 const original = `# key-value pair
12 key = "value"
13 list = ["item1", "item2", "item3"]
14
15 [map]
16 key = "value"
17
18 # nested
19 # map
20 [nested_map]
21 [nested_map.map]
22 key = "value"
23 list = [
24 "item1",
25 "item2",
26 "item3",
27 ]
28 `
29
30 // encoded form of the data
31 const encoded = `key = "value"
32 list = ["item1", "item2", "item3"]
33
34 [map]
35 key = "value"
36
37 [nested_map]
38
39 [nested_map.map]
40 key = "value"
41 list = ["item1", "item2", "item3"]
42 `
43
44 // Viper's internal representation
45 var data = map[string]interface{}{
46 "key": "value",
47 "list": []interface{}{
48 "item1",
49 "item2",
50 "item3",
51 },
52 "map": map[string]interface{}{
53 "key": "value",
54 },
55 "nested_map": map[string]interface{}{
56 "map": map[string]interface{}{
57 "key": "value",
58 "list": []interface{}{
59 "item1",
60 "item2",
61 "item3",
62 },
63 },
64 },
65 }
66
67 func TestCodec_Encode(t *testing.T) {
68 codec := Codec{}
69
70 b, err := codec.Encode(data)
71 if err != nil {
72 t.Fatal(err)
73 }
74
75 if encoded != string(b) {
76 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
77 }
78 }
79
80 func TestCodec_Decode(t *testing.T) {
81 t.Run("OK", func(t *testing.T) {
82 codec := Codec{}
83
84 v := map[string]interface{}{}
85
86 err := codec.Decode([]byte(original), v)
87 if err != nil {
88 t.Fatal(err)
89 }
90
91 if !reflect.DeepEqual(data, v) {
92 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
93 }
94 })
95
96 t.Run("InvalidData", func(t *testing.T) {
97 codec := Codec{}
98
99 v := map[string]interface{}{}
100
101 err := codec.Decode([]byte(`invalid data`), v)
102 if err == nil {
103 t.Fatal("expected decoding to fail")
104 }
105
106 t.Logf("decoding failed as expected: %s", err)
107 })
108 }
00 package yaml
11
2 import "gopkg.in/yaml.v2"
2 // import "gopkg.in/yaml.v2"
33
44 // Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
55 type Codec struct{}
66
7 func (Codec) Encode(v interface{}) ([]byte, error) {
7 func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
88 return yaml.Marshal(v)
99 }
1010
11 func (Codec) Decode(b []byte, v interface{}) error {
12 return yaml.Unmarshal(b, v)
11 func (Codec) Decode(b []byte, v map[string]interface{}) error {
12 return yaml.Unmarshal(b, &v)
1313 }
0 package yaml
1
2 import (
3 "reflect"
4 "testing"
5 )
6
7 func TestCodec_Encode(t *testing.T) {
8 codec := Codec{}
9
10 b, err := codec.Encode(data)
11 if err != nil {
12 t.Fatal(err)
13 }
14
15 if encoded != string(b) {
16 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
17 }
18 }
19
20 func TestCodec_Decode(t *testing.T) {
21 t.Run("OK", func(t *testing.T) {
22 codec := Codec{}
23
24 v := map[string]interface{}{}
25
26 err := codec.Decode([]byte(original), v)
27 if err != nil {
28 t.Fatal(err)
29 }
30
31 if !reflect.DeepEqual(decoded, v) {
32 t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
33 }
34 })
35
36 t.Run("InvalidData", func(t *testing.T) {
37 codec := Codec{}
38
39 v := map[string]interface{}{}
40
41 err := codec.Decode([]byte(`invalid data`), v)
42 if err == nil {
43 t.Fatal("expected decoding to fail")
44 }
45
46 t.Logf("decoding failed as expected: %s", err)
47 })
48 }
0 //go:build !viper_yaml3
1 // +build !viper_yaml3
2
3 package yaml
4
5 import yamlv2 "gopkg.in/yaml.v2"
6
7 var yaml = struct {
8 Marshal func(in interface{}) (out []byte, err error)
9 Unmarshal func(in []byte, out interface{}) (err error)
10 }{
11 Marshal: yamlv2.Marshal,
12 Unmarshal: yamlv2.Unmarshal,
13 }
0 //go:build !viper_yaml3
1 // +build !viper_yaml3
2
3 package yaml
4
5 // original form of the data
6 const original = `# key-value pair
7 key: value
8 list:
9 - item1
10 - item2
11 - item3
12 map:
13 key: value
14
15 # nested
16 # map
17 nested_map:
18 map:
19 key: value
20 list:
21 - item1
22 - item2
23 - item3
24 `
25
26 // encoded form of the data
27 const encoded = `key: value
28 list:
29 - item1
30 - item2
31 - item3
32 map:
33 key: value
34 nested_map:
35 map:
36 key: value
37 list:
38 - item1
39 - item2
40 - item3
41 `
42
43 // decoded form of the data
44 //
45 // in case of YAML it's slightly different from Viper's internal representation
46 // (eg. map is decoded into a map with interface key)
47 var decoded = map[string]interface{}{
48 "key": "value",
49 "list": []interface{}{
50 "item1",
51 "item2",
52 "item3",
53 },
54 "map": map[interface{}]interface{}{
55 "key": "value",
56 },
57 "nested_map": map[interface{}]interface{}{
58 "map": map[interface{}]interface{}{
59 "key": "value",
60 "list": []interface{}{
61 "item1",
62 "item2",
63 "item3",
64 },
65 },
66 },
67 }
68
69 // Viper's internal representation
70 var data = map[string]interface{}{
71 "key": "value",
72 "list": []interface{}{
73 "item1",
74 "item2",
75 "item3",
76 },
77 "map": map[string]interface{}{
78 "key": "value",
79 },
80 "nested_map": map[string]interface{}{
81 "map": map[string]interface{}{
82 "key": "value",
83 "list": []interface{}{
84 "item1",
85 "item2",
86 "item3",
87 },
88 },
89 },
90 }
0 //go:build viper_yaml3
1 // +build viper_yaml3
2
3 package yaml
4
5 import yamlv3 "gopkg.in/yaml.v3"
6
7 var yaml = struct {
8 Marshal func(in interface{}) (out []byte, err error)
9 Unmarshal func(in []byte, out interface{}) (err error)
10 }{
11 Marshal: yamlv3.Marshal,
12 Unmarshal: yamlv3.Unmarshal,
13 }
0 //go:build viper_yaml3
1 // +build viper_yaml3
2
3 package yaml
4
5 // original form of the data
6 const original = `# key-value pair
7 key: value
8 list:
9 - item1
10 - item2
11 - item3
12 map:
13 key: value
14
15 # nested
16 # map
17 nested_map:
18 map:
19 key: value
20 list:
21 - item1
22 - item2
23 - item3
24 `
25
26 // encoded form of the data
27 const encoded = `key: value
28 list:
29 - item1
30 - item2
31 - item3
32 map:
33 key: value
34 nested_map:
35 map:
36 key: value
37 list:
38 - item1
39 - item2
40 - item3
41 `
42
43 // decoded form of the data
44 //
45 // in case of YAML it's slightly different from Viper's internal representation
46 // (eg. map is decoded into a map with interface key)
47 var decoded = map[string]interface{}{
48 "key": "value",
49 "list": []interface{}{
50 "item1",
51 "item2",
52 "item3",
53 },
54 "map": map[string]interface{}{
55 "key": "value",
56 },
57 "nested_map": map[string]interface{}{
58 "map": map[string]interface{}{
59 "key": "value",
60 "list": []interface{}{
61 "item1",
62 "item2",
63 "item3",
64 },
65 },
66 },
67 }
68
69 // Viper's internal representation
70 var data = map[string]interface{}{
71 "key": "value",
72 "list": []interface{}{
73 "item1",
74 "item2",
75 "item3",
76 },
77 "map": map[string]interface{}{
78 "key": "value",
79 },
80 "nested_map": map[string]interface{}{
81 "map": map[string]interface{}{
82 "key": "value",
83 "list": []interface{}{
84 "item1",
85 "item2",
86 "item3",
87 },
88 },
89 },
90 }
+110
-150
viper.go less more
3434 "time"
3535
3636 "github.com/fsnotify/fsnotify"
37 "github.com/magiconair/properties"
3837 "github.com/mitchellh/mapstructure"
3938 "github.com/spf13/afero"
4039 "github.com/spf13/cast"
4140 "github.com/spf13/pflag"
42 "github.com/subosito/gotenv"
43 "gopkg.in/ini.v1"
4441
4542 "github.com/spf13/viper/internal/encoding"
43 "github.com/spf13/viper/internal/encoding/dotenv"
4644 "github.com/spf13/viper/internal/encoding/hcl"
45 "github.com/spf13/viper/internal/encoding/ini"
46 "github.com/spf13/viper/internal/encoding/javaproperties"
4747 "github.com/spf13/viper/internal/encoding/json"
4848 "github.com/spf13/viper/internal/encoding/toml"
4949 "github.com/spf13/viper/internal/encoding/yaml"
6666 Error error
6767 }
6868
69 var (
70 encoderRegistry = encoding.NewEncoderRegistry()
71 decoderRegistry = encoding.NewDecoderRegistry()
72 )
73
7469 func init() {
7570 v = New()
76
77 {
78 codec := yaml.Codec{}
79
80 encoderRegistry.RegisterEncoder("yaml", codec)
81 decoderRegistry.RegisterDecoder("yaml", codec)
82
83 encoderRegistry.RegisterEncoder("yml", codec)
84 decoderRegistry.RegisterDecoder("yml", codec)
85 }
86
87 {
88 codec := json.Codec{}
89
90 encoderRegistry.RegisterEncoder("json", codec)
91 decoderRegistry.RegisterDecoder("json", codec)
92 }
93
94 {
95 codec := toml.Codec{}
96
97 encoderRegistry.RegisterEncoder("toml", codec)
98 decoderRegistry.RegisterDecoder("toml", codec)
99 }
100
101 {
102 codec := hcl.Codec{}
103
104 encoderRegistry.RegisterEncoder("hcl", codec)
105 decoderRegistry.RegisterDecoder("hcl", codec)
106
107 encoderRegistry.RegisterEncoder("tfvars", codec)
108 decoderRegistry.RegisterDecoder("tfvars", codec)
109 }
11071 }
11172
11273 type remoteConfigFactory interface {
253214 aliases map[string]string
254215 typeByDefValue bool
255216
256 // Store read properties on the object so that we can write back in order with comments.
257 // This will only be used if the configuration read is a properties file.
258 properties *properties.Properties
259
260217 onConfigChange func(fsnotify.Event)
261218
262219 logger Logger
220
221 // TODO: should probably be protected with a mutex
222 encoderRegistry *encoding.EncoderRegistry
223 decoderRegistry *encoding.DecoderRegistry
263224 }
264225
265226 // New returns an initialized Viper instance.
279240 v.typeByDefValue = false
280241 v.logger = jwwLogger{}
281242
243 v.resetEncoding()
244
282245 return v
283246 }
284247
324287 for _, opt := range opts {
325288 opt.apply(v)
326289 }
290
291 v.resetEncoding()
327292
328293 return v
329294 }
335300 v = New()
336301 SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
337302 SupportedRemoteProviders = []string{"etcd", "consul", "firestore"}
303 }
304
305 // TODO: make this lazy initialization instead
306 func (v *Viper) resetEncoding() {
307 encoderRegistry := encoding.NewEncoderRegistry()
308 decoderRegistry := encoding.NewDecoderRegistry()
309
310 {
311 codec := yaml.Codec{}
312
313 encoderRegistry.RegisterEncoder("yaml", codec)
314 decoderRegistry.RegisterDecoder("yaml", codec)
315
316 encoderRegistry.RegisterEncoder("yml", codec)
317 decoderRegistry.RegisterDecoder("yml", codec)
318 }
319
320 {
321 codec := json.Codec{}
322
323 encoderRegistry.RegisterEncoder("json", codec)
324 decoderRegistry.RegisterDecoder("json", codec)
325 }
326
327 {
328 codec := toml.Codec{}
329
330 encoderRegistry.RegisterEncoder("toml", codec)
331 decoderRegistry.RegisterDecoder("toml", codec)
332 }
333
334 {
335 codec := hcl.Codec{}
336
337 encoderRegistry.RegisterEncoder("hcl", codec)
338 decoderRegistry.RegisterDecoder("hcl", codec)
339
340 encoderRegistry.RegisterEncoder("tfvars", codec)
341 decoderRegistry.RegisterDecoder("tfvars", codec)
342 }
343
344 {
345 codec := ini.Codec{
346 KeyDelimiter: v.keyDelim,
347 LoadOptions: v.iniLoadOptions,
348 }
349
350 encoderRegistry.RegisterEncoder("ini", codec)
351 decoderRegistry.RegisterDecoder("ini", codec)
352 }
353
354 {
355 codec := &javaproperties.Codec{
356 KeyDelimiter: v.keyDelim,
357 }
358
359 encoderRegistry.RegisterEncoder("properties", codec)
360 decoderRegistry.RegisterDecoder("properties", codec)
361
362 encoderRegistry.RegisterEncoder("props", codec)
363 decoderRegistry.RegisterDecoder("props", codec)
364
365 encoderRegistry.RegisterEncoder("prop", codec)
366 decoderRegistry.RegisterDecoder("prop", codec)
367 }
368
369 {
370 codec := &dotenv.Codec{}
371
372 encoderRegistry.RegisterEncoder("dotenv", codec)
373 decoderRegistry.RegisterDecoder("dotenv", codec)
374
375 encoderRegistry.RegisterEncoder("env", codec)
376 decoderRegistry.RegisterDecoder("env", codec)
377 }
378
379 v.encoderRegistry = encoderRegistry
380 v.decoderRegistry = decoderRegistry
338381 }
339382
340383 type defaultRemoteProvider struct {
432475 v.onConfigChange(event)
433476 }
434477 } else if filepath.Clean(event.Name) == configFile &&
435 event.Op&fsnotify.Remove&fsnotify.Remove != 0 {
478 event.Op&fsnotify.Remove != 0 {
436479 eventsWG.Done()
437480 return
438481 }
16331676 buf.ReadFrom(in)
16341677
16351678 switch format := strings.ToLower(v.getConfigType()); format {
1636 case "yaml", "yml", "json", "toml", "hcl", "tfvars":
1637 err := decoderRegistry.Decode(format, buf.Bytes(), &c)
1679 case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "properties", "props", "prop", "dotenv", "env":
1680 err := v.decoderRegistry.Decode(format, buf.Bytes(), c)
16381681 if err != nil {
16391682 return ConfigParseError{err}
1640 }
1641
1642 case "dotenv", "env":
1643 env, err := gotenv.StrictParse(buf)
1644 if err != nil {
1645 return ConfigParseError{err}
1646 }
1647 for k, v := range env {
1648 c[k] = v
1649 }
1650
1651 case "properties", "props", "prop":
1652 v.properties = properties.NewProperties()
1653 var err error
1654 if v.properties, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil {
1655 return ConfigParseError{err}
1656 }
1657 for _, key := range v.properties.Keys() {
1658 value, _ := v.properties.Get(key)
1659 // recursively build nested maps
1660 path := strings.Split(key, ".")
1661 lastKey := strings.ToLower(path[len(path)-1])
1662 deepestMap := deepSearch(c, path[0:len(path)-1])
1663 // set innermost value
1664 deepestMap[lastKey] = value
1665 }
1666
1667 case "ini":
1668 cfg := ini.Empty(v.iniLoadOptions)
1669 err := cfg.Append(buf.Bytes())
1670 if err != nil {
1671 return ConfigParseError{err}
1672 }
1673 sections := cfg.Sections()
1674 for i := 0; i < len(sections); i++ {
1675 section := sections[i]
1676 keys := section.Keys()
1677 for j := 0; j < len(keys); j++ {
1678 key := keys[j]
1679 value := cfg.Section(section.Name()).Key(key.Name()).String()
1680 c[section.Name()+"."+key.Name()] = value
1681 }
16821683 }
16831684 }
16841685
16901691 func (v *Viper) marshalWriter(f afero.File, configType string) error {
16911692 c := v.AllSettings()
16921693 switch configType {
1693 case "yaml", "yml", "json", "toml", "hcl", "tfvars":
1694 b, err := encoderRegistry.Encode(configType, c)
1694 case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "prop", "props", "properties", "dotenv", "env":
1695 b, err := v.encoderRegistry.Encode(configType, c)
16951696 if err != nil {
16961697 return ConfigMarshalError{err}
16971698 }
17001701 if err != nil {
17011702 return ConfigMarshalError{err}
17021703 }
1703
1704 case "prop", "props", "properties":
1705 if v.properties == nil {
1706 v.properties = properties.NewProperties()
1707 }
1708 p := v.properties
1709 for _, key := range v.AllKeys() {
1710 _, _, err := p.Set(key, v.GetString(key))
1711 if err != nil {
1712 return ConfigMarshalError{err}
1713 }
1714 }
1715 _, err := p.WriteComment(f, "#", properties.UTF8)
1716 if err != nil {
1717 return ConfigMarshalError{err}
1718 }
1719
1720 case "dotenv", "env":
1721 lines := []string{}
1722 for _, key := range v.AllKeys() {
1723 envName := strings.ToUpper(strings.Replace(key, ".", "_", -1))
1724 val := v.Get(key)
1725 lines = append(lines, fmt.Sprintf("%v=%v", envName, val))
1726 }
1727 s := strings.Join(lines, "\n")
1728 if _, err := f.WriteString(s); err != nil {
1729 return ConfigMarshalError{err}
1730 }
1731
1732 case "ini":
1733 keys := v.AllKeys()
1734 cfg := ini.Empty()
1735 ini.PrettyFormat = false
1736 for i := 0; i < len(keys); i++ {
1737 key := keys[i]
1738 lastSep := strings.LastIndex(key, ".")
1739 sectionName := key[:(lastSep)]
1740 keyName := key[(lastSep + 1):]
1741 if sectionName == "default" {
1742 sectionName = ""
1743 }
1744 cfg.Section(sectionName).Key(keyName).SetValue(v.GetString(key))
1745 }
1746 cfg.WriteTo(f)
17471704 }
17481705 return nil
17491706 }
18221779
18231780 svType := reflect.TypeOf(sv)
18241781 tvType := reflect.TypeOf(tv)
1825 if tvType != nil && svType != tvType { // Allow for the target to be nil
1826 v.logger.Error(
1827 "svType != tvType",
1828 "key", sk,
1829 "st", svType,
1830 "tt", tvType,
1831 "sv", sv,
1832 "tv", tv,
1833 )
1834 continue
1835 }
18361782
18371783 v.logger.Trace(
18381784 "processing",
18461792 switch ttv := tv.(type) {
18471793 case map[interface{}]interface{}:
18481794 v.logger.Trace("merging maps (must convert)")
1849 tsv := sv.(map[interface{}]interface{})
1795 tsv, ok := sv.(map[interface{}]interface{})
1796 if !ok {
1797 v.logger.Error(
1798 "Could not cast sv to map[interface{}]interface{}; key=%s, st=%v, tt=%v, sv=%v, tv=%v",
1799 sk, svType, tvType, sv, tv)
1800 continue
1801 }
1802
18501803 ssv := castToMapStringInterface(tsv)
18511804 stv := castToMapStringInterface(ttv)
18521805 mergeMaps(ssv, stv, ttv)
18531806 case map[string]interface{}:
18541807 v.logger.Trace("merging maps")
1855 mergeMaps(sv.(map[string]interface{}), ttv, nil)
1808 tsv, ok := sv.(map[string]interface{})
1809 if !ok {
1810 v.logger.Error(
1811 "Could not cast sv to map[string]interface{}; key=%s, st=%v, tt=%v, sv=%v, tv=%v",
1812 sk, svType, tvType, sv, tv)
1813 continue
1814 }
1815 mergeMaps(tsv, ttv, nil)
18561816 default:
18571817 v.logger.Trace("setting value")
18581818 tgt[tk] = sv
3232 "github.com/spf13/viper/internal/testutil"
3333 )
3434
35 var yamlExample = []byte(`Hacker: true
36 name: steve
37 hobbies:
38 - skateboarding
39 - snowboarding
40 - go
41 clothing:
42 jacket: leather
43 trousers: denim
44 pants:
45 size: large
46 age: 35
47 eyes : brown
48 beard: true
49 `)
35 // var yamlExample = []byte(`Hacker: true
36 // name: steve
37 // hobbies:
38 // - skateboarding
39 // - snowboarding
40 // - go
41 // clothing:
42 // jacket: leather
43 // trousers: denim
44 // pants:
45 // size: large
46 // age: 35
47 // eyes : brown
48 // beard: true
49 // `)
5050
5151 var yamlExampleWithExtras = []byte(`Existing: true
5252 Bogus: true
15571557 p_batters.batter.type = Regular
15581558 `)
15591559
1560 var yamlWriteExpected = []byte(`age: 35
1561 beard: true
1562 clothing:
1563 jacket: leather
1564 pants:
1565 size: large
1566 trousers: denim
1567 eyes: brown
1568 hacker: true
1569 hobbies:
1570 - skateboarding
1571 - snowboarding
1572 - go
1573 name: steve
1574 `)
1560 // var yamlWriteExpected = []byte(`age: 35
1561 // beard: true
1562 // clothing:
1563 // jacket: leather
1564 // pants:
1565 // size: large
1566 // trousers: denim
1567 // eyes: brown
1568 // hacker: true
1569 // hobbies:
1570 // - skateboarding
1571 // - snowboarding
1572 // - go
1573 // name: steve
1574 // `)
15751575
15761576 func TestWriteConfig(t *testing.T) {
15771577 fs := afero.NewMemMapFs()
19111911 fu: bar
19121912 `)
19131913
1914 var jsonMergeExampleTgt = []byte(`
1915 {
1916 "hello": {
1917 "foo": null,
1918 "pop": 123456
1919 }
1920 }
1921 `)
1922
1923 var jsonMergeExampleSrc = []byte(`
1924 {
1925 "hello": {
1926 "foo": "foo str",
1927 "pop": "pop str"
1928 }
1929 }
1930 `)
1931
19141932 func TestMergeConfig(t *testing.T) {
19151933 v := New()
19161934 v.SetConfigType("yml")
19801998
19811999 if fu := v.GetString("fu"); fu != "bar" {
19822000 t.Fatalf("fu != \"bar\", = %s", fu)
2001 }
2002 }
2003
2004 func TestMergeConfigOverrideType(t *testing.T) {
2005 v := New()
2006 v.SetConfigType("json")
2007 if err := v.ReadConfig(bytes.NewBuffer(jsonMergeExampleTgt)); err != nil {
2008 t.Fatal(err)
2009 }
2010
2011 if err := v.MergeConfig(bytes.NewBuffer(jsonMergeExampleSrc)); err != nil {
2012 t.Fatal(err)
2013 }
2014
2015 if pop := v.GetString("hello.pop"); pop != "pop str" {
2016 t.Fatalf("pop != \"pop str\", = %s", pop)
2017 }
2018
2019 if foo := v.GetString("hello.foo"); foo != "foo str" {
2020 t.Fatalf("foo != \"foo str\", = %s", foo)
19832021 }
19842022 }
19852023
24092447 assert.Equal(t, "cobra_flag", config.Foo.Bar)
24102448 }
24112449
2412 var yamlExampleWithDot = []byte(`Hacker: true
2413 name: steve
2414 hobbies:
2415 - skateboarding
2416 - snowboarding
2417 - go
2418 clothing:
2419 jacket: leather
2420 trousers: denim
2421 pants:
2422 size: large
2423 age: 35
2424 eyes : brown
2425 beard: true
2426 emails:
2427 steve@hacker.com:
2428 created: 01/02/03
2429 active: true
2430 `)
2450 // var yamlExampleWithDot = []byte(`Hacker: true
2451 // name: steve
2452 // hobbies:
2453 // - skateboarding
2454 // - snowboarding
2455 // - go
2456 // clothing:
2457 // jacket: leather
2458 // trousers: denim
2459 // pants:
2460 // size: large
2461 // age: 35
2462 // eyes : brown
2463 // beard: true
2464 // emails:
2465 // steve@hacker.com:
2466 // created: 01/02/03
2467 // active: true
2468 // `)
24312469
24322470 func TestKeyDelimiter(t *testing.T) {
24332471 v := NewWithOptions(KeyDelimiter("::"))
0 //go:build !viper_yaml3
1 // +build !viper_yaml3
2
3 package viper
4
5 var yamlExample = []byte(`Hacker: true
6 name: steve
7 hobbies:
8 - skateboarding
9 - snowboarding
10 - go
11 clothing:
12 jacket: leather
13 trousers: denim
14 pants:
15 size: large
16 age: 35
17 eyes : brown
18 beard: true
19 `)
20
21 var yamlWriteExpected = []byte(`age: 35
22 beard: true
23 clothing:
24 jacket: leather
25 pants:
26 size: large
27 trousers: denim
28 eyes: brown
29 hacker: true
30 hobbies:
31 - skateboarding
32 - snowboarding
33 - go
34 name: steve
35 `)
36
37 var yamlExampleWithDot = []byte(`Hacker: true
38 name: steve
39 hobbies:
40 - skateboarding
41 - snowboarding
42 - go
43 clothing:
44 jacket: leather
45 trousers: denim
46 pants:
47 size: large
48 age: 35
49 eyes : brown
50 beard: true
51 emails:
52 steve@hacker.com:
53 created: 01/02/03
54 active: true
55 `)
0 //go:build viper_yaml3
1 // +build viper_yaml3
2
3 package viper
4
5 var yamlExample = []byte(`Hacker: true
6 name: steve
7 hobbies:
8 - skateboarding
9 - snowboarding
10 - go
11 clothing:
12 jacket: leather
13 trousers: denim
14 pants:
15 size: large
16 age: 35
17 eyes : brown
18 beard: true
19 `)
20
21 var yamlWriteExpected = []byte(`age: 35
22 beard: true
23 clothing:
24 jacket: leather
25 pants:
26 size: large
27 trousers: denim
28 eyes: brown
29 hacker: true
30 hobbies:
31 - skateboarding
32 - snowboarding
33 - go
34 name: steve
35 `)
36
37 var yamlExampleWithDot = []byte(`Hacker: true
38 name: steve
39 hobbies:
40 - skateboarding
41 - snowboarding
42 - go
43 clothing:
44 jacket: leather
45 trousers: denim
46 pants:
47 size: large
48 age: 35
49 eyes : brown
50 beard: true
51 emails:
52 steve@hacker.com:
53 created: 01/02/03
54 active: true
55 `)