Full Code of spf13/viper for AI

master 528f7416c4b5 cached
63 files
272.1 KB
89.5k tokens
436 symbols
1 requests
Download .txt
Showing preview only (289K chars total). Download the full file or copy to clipboard to get everything.
Repository: spf13/viper
Branch: master
Commit: 528f7416c4b5
Files: 63
Total size: 272.1 KB

Directory structure:
gitextract_5z5ozi0n/

├── .editorconfig
├── .envrc
├── .github/
│   ├── .editorconfig
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yaml
│   │   ├── config.yml
│   │   └── feature_request.yaml
│   ├── PULL_REQUEST_TEMPLATES.md
│   ├── dependabot.yaml
│   ├── octoslash/
│   │   ├── policies/
│   │   │   ├── collaborator.cedar
│   │   │   └── triager.cedar
│   │   └── principals.json
│   ├── release.yml
│   └── workflows/
│       ├── checks.yaml
│       ├── ci.yaml
│       ├── octoslash.yaml
│       └── stale.yaml
├── .gitignore
├── .golangci.yaml
├── .yamlignore
├── .yamllint.yaml
├── LICENSE
├── Makefile
├── README.md
├── TROUBLESHOOTING.md
├── UPGRADE.md
├── encoding.go
├── encoding_test.go
├── errors.go
├── experimental.go
├── file.go
├── finder.go
├── finder_example_test.go
├── finder_test.go
├── flags.go
├── flags_test.go
├── flake.nix
├── go.mod
├── go.sum
├── internal/
│   ├── encoding/
│   │   ├── dotenv/
│   │   │   ├── codec.go
│   │   │   ├── codec_test.go
│   │   │   └── map_utils.go
│   │   ├── json/
│   │   │   ├── codec.go
│   │   │   └── codec_test.go
│   │   ├── toml/
│   │   │   ├── codec.go
│   │   │   └── codec_test.go
│   │   └── yaml/
│   │       ├── codec.go
│   │       └── codec_test.go
│   ├── features/
│   │   ├── bind_struct.go
│   │   ├── bind_struct_default.go
│   │   ├── finder.go
│   │   └── finder_default.go
│   └── testutil/
│       └── filepath.go
├── logger.go
├── overrides_test.go
├── remote/
│   ├── go.mod
│   ├── go.sum
│   └── remote.go
├── remote.go
├── util.go
├── util_test.go
├── viper.go
├── viper_test.go
└── viper_yaml_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[*.go]
indent_style = tab

[{Makefile,*.mk}]
indent_style = tab

[*.nix]
indent_size = 2

[.golangci.yaml]
indent_size = 2


================================================
FILE: .envrc
================================================
if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
  source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4="
fi
use flake . --impure


================================================
FILE: .github/.editorconfig
================================================
[{*.yml,*.yaml}]
indent_size = 2


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yaml
================================================
name: 🐛 Bug report
description: Report a bug to help us improve Viper
labels: [kind/bug]
body:
- type: markdown
  attributes:
    value: |
      Thank you for submitting a bug report!

      Please fill out the template below to make it easier to debug your problem.

      If you are not sure if it is a bug or not, you can contact us via the available [support channels](https://github.com/spf13/viper/issues/new/choose).
- type: checkboxes
  attributes:
    label: Preflight Checklist
    description: Please ensure you've completed all of the following.
    options:
      - 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.
        required: true
      - label: I am not looking for support or already pursued the available [support channels](https://github.com/spf13/viper/issues/new/choose) without success.
        required: true
      - label: I have checked the [troubleshooting guide](https://github.com/spf13/viper/blob/master/TROUBLESHOOTING.md) for my problem, without success.
        required: true
- type: input
  attributes:
    label: Viper Version
    description: What version of Viper are you using?
    placeholder: 1.8.1
  validations:
    required: true
- type: input
  attributes:
    label: Go Version
    description: What version of Go are you using?
    placeholder: "1.16"
  validations:
    required: true
- type: dropdown
  attributes:
    label: Config Source
    description: What sources do you load configuration from?
    options:
      - Manual set
      - Flags
      - Environment variables
      - Files
      - Remove K/V stores
      - Defaults
    multiple: true
  validations:
    required: true
- type: dropdown
  attributes:
    label: Format
    description: Which file formats do you use?
    options:
      - JSON
      - YAML
      - TOML
      - Dotenv
      - HCL
      - Java properties
      - INI
      - Other (specify below)
    multiple: true
- type: input
  attributes:
    label: Repl.it link
    description: Complete example on Repl.it reproducing the issue. [Here](https://repl.it/@sagikazarmark/Viper-example) is an example you can use.
    placeholder: https://repl.it/@sagikazarmark/Viper-example
- type: textarea
  attributes:
    label: Code reproducing the issue
    description: Please provide a Repl.it link if possible.
    render: go
    placeholder: |
      package main

      import (
        "github.com/spf13/viper"
      )

      func main() {
        v := viper.New()

        // ...

        var config Config

        err = v.Unmarshal(&config)
        if err != nil {
          panic(err)
        }
      }
- type: textarea
  attributes:
    label: Expected Behavior
    description: A clear and concise description of what you expected to happen.
  validations:
    required: true
- type: textarea
  attributes:
    label: Actual Behavior
    description: A clear description of what actually happens.
  validations:
    required: true
- type: textarea
  attributes:
    label: Steps To Reproduce
    description: Steps to reproduce the behavior if it is not self-explanatory.
    placeholder: |
      1. In this environment...
      2. With this config...
      3. Run '...'
      4. See error...
- type: textarea
  attributes:
    label: Additional Information
    description: Links? References? Anything that will give us more context about the issue that you are encountering!


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: ❓ Ask a question
    url: https://github.com/spf13/viper/discussions/new?category=q-a
    about:  Ask and discuss questions with other Viper community members

  - name: 📓 Reference
    url: https://pkg.go.dev/mod/github.com/spf13/viper
    about: Check the Go code reference

  - name: 💬 Slack channel
    url: https://gophers.slack.com/messages/viper
    about: Please ask and answer questions here


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yaml
================================================
name: 🎉 Feature request
description: Suggest an idea for Viper
labels: [kind/enhancement]
body:
- type: markdown
  attributes:
    value: |
      Thank you for submitting a feature request!

      Please describe what you would like to change/add and why in detail by filling out the template below.

      If you are not sure if your request fits into Viper, you can contact us via the available [support channels](https://github.com/spf13/viper/issues/new/choose).
- type: checkboxes
  attributes:
    label: Preflight Checklist
    description: Please ensure you've completed all of the following.
    options:
      - 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.
        required: true
- type: textarea
  attributes:
    label: Problem Description
    description: A clear and concise description of the problem you are seeking to solve with this feature request.
  validations:
    required: true
- type: textarea
  attributes:
    label: Proposed Solution
    description: A clear and concise description of what would you like to happen.
  validations:
    required: true
- type: textarea
  attributes:
    label: Alternatives Considered
    description: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
  attributes:
    label: Additional Information
    description: Add any other context about the problem here.


================================================
FILE: .github/PULL_REQUEST_TEMPLATES.md
================================================
<!--
Thank you for sending a pull request! Here some tips for contributors:

1. Fill the description template below.
2. Include appropriate tests (if necessary). Make sure that all CI checks passed.
3. If the Pull Request is a work in progress, make use of GitHub's "Draft PR" feature and mark it as such.
-->

**Overview**:
<!-- Describe your changes briefly here. -->

**What problem does it solve?**:
<!--
- Please state in detail why we need this PR and what it solves.
- If your PR closes some of the existing issues, please add links to them here.
  Mentioned issues will be automatically closed.
  Usage: "Closes #<issue number>", or "Closes (paste link of issue)"
-->

**Special notes for a reviewer**:


================================================
FILE: .github/dependabot.yaml
================================================
version: 2

updates:
  - package-ecosystem: gomod
    directory: /
    labels:
      - area/dependencies
    schedule:
      interval: daily

  - package-ecosystem: github-actions
    directory: /
    labels:
      - area/dependencies
    schedule:
      interval: daily


================================================
FILE: .github/octoslash/policies/collaborator.cedar
================================================
permit(
	principal in Role::"Collaborator",
	action,
	resource
);


================================================
FILE: .github/octoslash/policies/triager.cedar
================================================
permit(
	principal in Role::"Triager",
	action in [Action::"close", Action::"add-label", Action::"remove-label", Action::"self-assign", Action::"self-unassign"],
	resource
);


================================================
FILE: .github/octoslash/principals.json
================================================
[
    {
        "uid": { "type": "User", "id": "1226384" },
        "attrs": { "login": "sagikazarmark" },
        "parents": [{ "type": "Role", "id": "Collaborator" }]
    },
    {
        "uid": { "type": "User", "id": "805695" },
        "attrs": { "login": "spacez320" },
        "parents": [{ "type": "Role", "id": "Triager" }]
    }
]


================================================
FILE: .github/release.yml
================================================
changelog:
  exclude:
    labels:
      - release-note/ignore
  categories:
    - title: Exciting New Features 🎉
      labels:
        - kind/feature
        - release-note/new-feature
    - title: Enhancements 🚀
      labels:
        - kind/enhancement
        - release-note/enhancement
    - title: Bug Fixes 🐛
      labels:
        - kind/bug
        - release-note/bug-fix
    - title: Breaking Changes 🛠
      labels:
        - release-note/breaking-change
    - title: Deprecations ❌
      labels:
        - release-note/deprecation
    - title: Dependency Updates ⬆️
      labels:
        - area/dependencies
        - release-note/dependency-update
    - title: Other Changes
      labels:
        - "*"


================================================
FILE: .github/workflows/checks.yaml
================================================
name: PR Checks

on:
  pull_request:
    types: [opened, labeled, unlabeled, synchronize]

permissions:
  pull-requests: read

jobs:
  release-label:
    name: Release note label
    runs-on: ubuntu-latest

    steps:
      - name: Check minimum labels
        uses: mheap/github-action-required-labels@388fd6af37b34cdfe5a23b37060e763217e58b03 # v5.5
        with:
          mode: minimum
          count: 1
          labels: |
            release-note/ignore
            kind/feature
            release-note/new-feature
            kind/enhancement
            release-note/enhancement
            kind/bug
            release-note/bug-fix
            release-note/breaking-change
            release-note/deprecation
            area/dependencies
            release-note/dependency-update
            release-note/misc


================================================
FILE: .github/workflows/ci.yaml
================================================
name: CI

on:
  push:
    branches: [master]
  pull_request:

permissions:
  contents: read

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        include:
          - goos: js
            goarch: wasm
          - goos: aix
            goarch: ppc64

    steps:
      - name: Checkout repository
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Set up Go
        uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
        with:
          go-version: "1.25"

      - name: Build
        run: go build .
        env:
          GOOS: ${{ matrix.goos }}
          GOARCH: ${{ matrix.goarch }}

  test:
    name: Test
    runs-on: ${{ matrix.os }}

    strategy:
      # Fail fast is disabled because there are Go version specific features and tests
      # that should be able to fail independently.
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        go: ["1.23", "1.24", "1.25"]
        tags: ["", "viper_finder", "viper_bind_struct"]

    steps:
      - name: Checkout repository
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Set up Go
        uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
        with:
          go-version: ${{ matrix.go }}

      - name: Test
        run: go test -race -v -tags '${{ matrix.tags }}' -shuffle=on ./...
        if: runner.os != 'Windows'

      - name: Test (without race detector)
        run: go test -v -tags '${{ matrix.tags }}' -shuffle=on ./...
        if: runner.os == 'Windows'

  lint:
    name: Lint
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Set up Go
        uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
        with:
          go-version: "1.25"

      - name: Lint
        uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
        with:
          version: v2.4.0

  dev:
    name: Developer environment
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Set up Nix
        uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # v27
        with:
          extra_nix_config: |
            access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}

      - name: Check
        run: nix flake check --impure

      - name: Dev shell
        run: nix develop --impure

  dependency-review:
    name: Dependency review
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'

    steps:
      - name: Checkout repository
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Dependency Review
        uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0


================================================
FILE: .github/workflows/octoslash.yaml
================================================
name: Octoslash

on: issue_comment

permissions:
  issues: write
  pull-requests: write

jobs:
  run:
    name: Run
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Octoslash
        uses: sagikazarmark/octoslash-action@v0


================================================
FILE: .github/workflows/stale.yaml
================================================
name: "Close stale issues"

on:
  schedule:
    - cron: "0 3 * * *"

permissions:
  actions: write
  issues: write
  # contents: write # only for delete-branch option
  # pull-requests: write

jobs:
  stale:
    name: Stale
    runs-on: ubuntu-latest

    steps:
      - uses: actions/stale@v9
        with:
          days-before-stale: 30
          days-before-close: 5
          days-before-pr-stale: -1 # Ignore all PRs
          stale-issue-message: "Issues with no activity for 30 days are marked stale and subject to being closed."
          close-issue-message: "This issue has been closed for inactivity. Please re-open if this was a mistake."
          exempt-issue-labels: "lifecycle/keep"
          stale-issue-label: "lifecycle/stale"
          operations-per-run: 500 # Must respect GitHub API limits
          debug-only: false # Set this to 'true' to disable stale issue management


================================================
FILE: .gitignore
================================================
/.devenv/
/.direnv/
/.idea/
/.pre-commit-config.yaml
/bin/
/build/
/var/
/vendor/


================================================
FILE: .golangci.yaml
================================================
version: "2"

run:
  timeout: 5m

linters:
  enable:
    - bodyclose
    - dogsled
    - dupl
    - durationcheck
    - exhaustive
    - gocritic
    - godot
    - gomoddirectives
    - goprintffuncname
    - govet
    - importas
    - ineffassign
    - makezero
    - misspell
    - nakedret
    - nilerr
    - noctx
    - nolintlint
    - prealloc
    - predeclared
    - revive
    - rowserrcheck
    - sqlclosecheck
    - staticcheck
    - tparallel
    - unconvert
    - unparam
    - unused
    - wastedassign
    - whitespace

    # fixme
    # - cyclop
    # - errcheck
    # - errorlint
    # - exhaustivestruct
    # - forbidigo
    # - forcetypeassert
    # - gochecknoglobals
    # - gochecknoinits
    # - gocognit
    # - goconst
    # - gocyclo
    # - gosec
    # - gosimple
    # - ifshort
    # - lll
    # - nlreturn
    # - paralleltest
    # - scopelint
    # - thelper
    # - wrapcheck

    # unused
    # - depguard
    # - goheader
    # - gomodguard

    # don't enable:
    # - asciicheck
    # - funlen
    # - godox
    # - goerr113
    # - gomnd
    # - interfacer
    # - maligned
    # - nestif
    # - testpackage
    # - wsl

  exclusions:
    rules:
      - linters:
        - errcheck
        - noctx
        path: _test.go
    presets:
      - std-error-handling

  settings:
    revive:
      rules:
        - name: blank-imports
        - name: context-as-argument
          arguments:
            - allowTypesBefore: "*testing.T"
        - name: context-keys-type
        - name: dot-imports
        - name: empty-block
        - name: error-naming
        - name: error-return
        - name: error-strings
        - name: errorf
        - name: exported
          arguments:
            - "sayRepetitiveInsteadOfStutters"  # make error messages clearer
        - name: increment-decrement
        - name: indent-error-flow
        - name: package-comments
          disabled: true # disable package comments rule for now
        - name: range
        - name: redefines-builtin-id
        - name: superfluous-else
        - name: time-naming
        - name: unexported-return
        - name: unreachable-code
        - name: unused-parameter
        - name: var-declaration
        - name: var-naming

        # TODO: add more revive rules

    misspell:
      locale: US
    nolintlint:
      allow-unused: false # report any unused nolint directives
      require-specific: false # don't require nolint directives to be specific about which linter is being skipped
    gocritic:
      # Enable multiple checks by tags. See "Tags" section in https://github.com/go-critic/go-critic#usage.
      enabled-tags:
        - diagnostic
        - experimental
        - opinionated
        - style
      disabled-checks:
        - importShadow
        - unnamedResult

formatters:
  enable:
    - gci
    - gofmt
    - gofumpt
    - goimports
    # - golines

  settings:
    gci:
      sections:
        - standard
        - default
        - localmodule


================================================
FILE: .yamlignore
================================================
# TODO: FIXME
/.github/


================================================
FILE: .yamllint.yaml
================================================
ignore-from-file: [.gitignore, .yamlignore]

extends: default

rules:
  line-length: disable


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014 Steve Francia

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: Makefile
================================================
# A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html

OS = $(shell uname | tr A-Z a-z)
export PATH := $(abspath bin/):${PATH}

# Build variables
BUILD_DIR ?= build
export CGO_ENABLED ?= 0
export GOOS = $(shell go env GOOS)
ifeq (${VERBOSE}, 1)
ifeq ($(filter -v,${GOARGS}),)
	GOARGS += -v
endif
TEST_FORMAT = short-verbose
endif

# Dependency versions
GOTESTSUM_VERSION = 1.9.0
GOLANGCI_VERSION = 1.53.3

# Add the ability to override some variables
# Use with care
-include override.mk

.PHONY: clear
clear: ## Clear the working area and the project
	rm -rf bin/

.PHONY: check
check: test lint ## Run tests and linters


TEST_PKGS ?= ./...
.PHONY: test
test: TEST_FORMAT ?= short
test: SHELL = /bin/bash
test: export CGO_ENABLED=1
test: bin/gotestsum ## Run tests
	@mkdir -p ${BUILD_DIR}
	bin/gotestsum --no-summary=skipped --junitfile ${BUILD_DIR}/coverage.xml --format ${TEST_FORMAT} -- -race -coverprofile=${BUILD_DIR}/coverage.txt -covermode=atomic $(filter-out -v,${GOARGS}) $(if ${TEST_PKGS},${TEST_PKGS},./...)

.PHONY: lint
lint: lint-go lint-yaml
lint: ## Run linters

.PHONY: lint-go
lint-go:
	golangci-lint run $(if ${CI},--out-format github-actions,)

.PHONY: lint-yaml
lint-yaml:
	yamllint $(if ${CI},-f github,) --no-warnings .

.PHONY: fmt
fmt: ## Format code
	golangci-lint run --fix

deps: bin/golangci-lint bin/gotestsum yamllint
deps: ## Install dependencies

bin/gotestsum:
	@mkdir -p bin
	curl -L https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_${OS}_amd64.tar.gz | tar -zOxf - gotestsum > ./bin/gotestsum && chmod +x ./bin/gotestsum

bin/golangci-lint:
	@mkdir -p bin
	curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- v${GOLANGCI_VERSION}

.PHONY: yamllint
yamllint:
	pip3 install --user yamllint

# Add custom targets here
-include custom.mk

.PHONY: list
list: ## List all make targets
	@${MAKE} -pRrn : -f $(MAKEFILE_LIST) 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | sort

.PHONY: help
.DEFAULT_GOAL := help
help:
	@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

# Variable outputting/exporting rules
var-%: ; @echo $($*)
varexport-%: ; @echo $*=$($*)


================================================
FILE: README.md
================================================
> ## Viper v2 Feedback
>
> Viper is heading towards v2 and we would love to hear what _**you**_ would
> like to see in it. Share your thoughts here:
> https://forms.gle/R6faU74qPRPAzchZ9
>
> **Thank you!**

![viper logo](https://github.com/user-attachments/assets/acae9193-2974-41f3-808d-2d433f5ada5e)

[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration)
[![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go)

[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/viper/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI)
[![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper)
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.23-61CFDD.svg?style=flat-square)
[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper)

**Go configuration with fangs!**

Many Go projects are built using Viper including:

* [Hugo](http://gohugo.io)
* [EMC RexRay](http://rexray.readthedocs.org/en/stable/)
* [Imgur’s Incus](https://github.com/Imgur/incus)
* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)
* [Docker Notary](https://github.com/docker/Notary)
* [BloomApi](https://www.bloomapi.com/)
* [doctl](https://github.com/digitalocean/doctl)
* [Clairctl](https://github.com/jgsqware/clairctl)
* [Mercure](https://mercure.rocks)
* [Meshery](https://github.com/meshery/meshery)
* [Bearer](https://github.com/bearer/bearer)
* [Coder](https://github.com/coder/coder)
* [Vitess](https://vitess.io/)


## Install

```shell
go get github.com/spf13/viper
```

> **NOTE** Viper uses [Go Modules](https://go.dev/wiki/Modules) to manage dependencies.


## Why use Viper?

Viper is a complete configuration solution for Go applications including
[12-Factor apps](https://12factor.net/#the_twelve_factors). It is designed to
work within any application, and can handle all types of configuration needs
and formats. It supports:

* setting defaults
* setting explicit values
* reading config files
* dynamic discovery of config files across multiple locations
* reading from environment variables
* reading from remote systems (e.g. Etcd or Consul)
* reading from command line flags
* reading from buffers
* live watching and updating configuration
* aliasing configuration keys for easy refactoring

Viper can be thought of as a registry for all of your applications'
configuration needs.


## Putting Values in Viper

Viper can read from multiple configuration sources and merges them together
into one set of configuration keys and values.

Viper uses the following precedence for merging:

 * explicit call to `Set`
 * flags
 * environment variables
 * config files
 * external key/value stores
 * defaults

> **NOTE** Viper configuration keys are case insensitive.

### Reading Config Files

Viper requires minimal configuration to load config files. Viper currently supports:

* JSON
* TOML
* YAML
* INI
* envfile
* Java Propeties

A single Viper instance only supports a single configuration file, but multiple
paths may be searched for one.

Here is an example of how to use Viper to search for and read a configuration
file. At least one path should be provided where a configuration file is
expected.

```go
// Name of the config file without an extension (Viper will intuit the type
// from an extension on the actual file)
viper.SetConfigName("config")

// Add search paths to find the file
viper.AddConfigPath("/etc/appname/")
viper.AddConfigPath("$HOME/.appname")
viper.AddConfigPath(".")

// Find and read the config file
err := viper.ReadInConfig()

// Handle errors
if err != nil {
	panic(fmt.Errorf("fatal error config file: %w", err))
}
```

You can handle the specific case where no config file is found.

```go
var fileLookupError viper.FileLookupError
if err := viper.ReadInConfig(); err != nil {
    if errors.As(err, &fileLookupError) {
        // Indicates an explicitly set config file is not found (such as with
        // using `viper.SetConfigFile`) or that no config file was found in
        // any search path (such as when using `viper.AddConfigPath`)
    } else {
        // Config file was found but another error was produced
    }
}

// Config file found and successfully parsed
```

> **NOTE (since 1.6)** You can also have a file without an extension and
> specify the format programmatically, which is useful for files that naturally
> have no extension (e.g., `.bashrc`).

### Writing Config Files

At times you may want to store all configuration modifications made during run
time.

```go
// Writes current config to the path set by `AddConfigPath` and `SetConfigName`
viper.WriteConfig()
viper.SafeWriteConfig() // Like the above, but will error if the config file exists

// Writes current config to a specific place
viper.WriteConfigAs("/path/to/my/.config")

// Will error since it has already been written
viper.SafeWriteConfigAs("/path/to/my/.config")

viper.SafeWriteConfigAs("/path/to/my/.other_config")
```

As a rule of the thumb, methods prefixed with `Safe` won't overwrite any
existing file, while other methods will.

### Watching and Re-reading Config Files

Gone are the days of needing to restart a server to have a config take
effect--Viper powered applications can read an update to a config file while
running and not miss a beat.

It's also possible to provide a function for Viper to run each time a change
occurs.

```go
// All config paths must be defined prior to calling `WatchConfig()`
viper.AddConfigPath("$HOME/.appname")

viper.OnConfigChange(func(e fsnotify.Event) {
	fmt.Println("Config file changed:", e.Name)
})

viper.WatchConfig()
```

### Reading Config from `io.Reader`

Viper predefines many configuration sources but you can also implement your own
required configuration source.

```go
viper.SetConfigType("yaml")

var yamlExample = []byte(`
hacker: true
hobbies:
- skateboarding
- snowboarding
- go
name: steve
`)

viper.ReadConfig(bytes.NewBuffer(yamlExample))

viper.Get("name") // "steve"
```

### Setting Defaults

A good configuration system will support default values, which are used if a
key hasn't been set in some other way.

Examples:

```go
viper.SetDefault("ContentDir", "content")
viper.SetDefault("LayoutDir", "layouts")
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
```

### Setting Overrides

Viper allows explict setting of configuration, such as from your own
application logic.

```go
viper.Set("verbose", true)
viper.Set("host.port", 5899) // Set an embedded key
```

### Registering and Using Aliases

Aliases permit a single value to be referenced by multiple keys

```go
viper.RegisterAlias("loud", "Verbose")

viper.Set("verbose", true) // Same result as next line
viper.Set("loud", true)    // Same result as prior line

viper.GetBool("loud")    // true
viper.GetBool("verbose") // true
```

### Working with Environment Variables

Viper has full support for environment variables.

> **NOTE** Unlike other configuration sources, environment variables are case
> sensitive.

```go
// Tells Viper to use this prefix when reading environment variables
viper.SetEnvPrefix("spf")

// Viper will look for "SPF_ID", automatically uppercasing the prefix and key
viper.BindEnv("id")

// Alternatively, we can search for any environment variable prefixed and load
// them in
viper.AutomaticEnv()

os.Setenv("SPF_ID", "13")

id := viper.Get("id") // 13
```

* By default, empty environment variables are considered unset and will fall back to
  the next configuration source, unless `AllowEmptyEnv` is used.
* Viper does not "cache" environment variables--the value will be read each
  time it is accessed.
* `SetEnvKeyReplacer` and `EnvKeyReplacer` allow you to rewrite environment
  variable keys, which is useful to merge SCREAMING_SNAKE_CASE environment
  variables with kebab-cased configuration values from other sources.

### Working with Flags

Viper has the ability to bind to flags. Specifically, Viper supports
[pflag](https://github.com/spf13/pflag/) as used in the
[Cobra](https://github.com/spf13/cobra) library.

Like environment variables, the value is not set when the binding method is
called, but when it is accessed.

For individual flags, the `BindPFlag` method provides this functionality.

```go
serverCmd.Flags().Int("port", 1138, "Port to run Application server on")

viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
```

You can also bind an existing set of pflags.

```go
pflag.Int("flagname", 1234, "help message for flagname")
pflag.Parse()

viper.BindPFlags(pflag.CommandLine)

i := viper.GetInt("flagname") // Retrieve values from viper instead of pflag
```

The standard library [flag](https://golang.org/pkg/flag/) package is not
directly supported, but may be parsed through pflag.

```go
package main

import (
	"flag"

	"github.com/spf13/pflag"
)

func main() {
	// Using standard library "flag" package
	flag.Int("flagname", 1234, "help message for flagname")

    // Pass standard library flags to pflag
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
	pflag.Parse()

    // Viper takes over
	viper.BindPFlags(pflag.CommandLine)
}
```

Use of pflag may be avoided entirely by implementing the `FlagValue` and
`FlagValueSet` interfaces.

```go
// Implementing FlagValue

type myFlag struct {}
func (f myFlag) HasChanged() bool { return false }
func (f myFlag) Name() string { return "my-flag-name" }
func (f myFlag) ValueString() string { return "my-flag-value" }
func (f myFlag) ValueType() string { return "string" }

viper.BindFlagValue("my-flag-name", myFlag{})

// Implementing FlagValueSet

type myFlagSet struct {
	flags []myFlag
}
func (f myFlagSet) VisitAll(fn func(FlagValue)) {
	for _, flag := range flags {
		fn(flag)
	}
}

fSet := myFlagSet{
	flags: []myFlag{myFlag{}, myFlag{}},
}
viper.BindFlagValues("my-flags", fSet)
```

### Remote Key/Value Store Support

To enable remote support in Viper, do a blank import of the `viper/remote`
package.

```go
import _ "github.com/spf13/viper/remote"
```

Viper supports the following remote key/value stores. Examples for each are
provided below.

* Etcd and Etcd3
* Consul
* Firestore
* NATS

Viper will read a config string retrieved from a path in a key/value store.

Viper supports multiple hosts separated by `;`. For example:
`http://127.0.0.1:4001;http://127.0.0.1:4002`.

#### Encryption

Viper uses [crypt](https://github.com/sagikazarmark/crypt) to retrieve
configuration from the key/value store, which means that you can store your
configuration values encrypted and have them automatically decrypted if you
have the correct GPG keyring. Encryption is optional.

Crypt has a command-line helper that you can use to put configurations in your
key/value store.

```bash
$ go get github.com/sagikazarmark/crypt/bin/crypt
$ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json
$ crypt get -plaintext /config/hugo.json
```

See the Crypt documentation for examples of how to set encrypted values, or
how to use Consul.

### Remote Key/Value Store Examples (Unencrypted)

#### etcd

```go
viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json")
viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
err := viper.ReadRemoteConfig()
```

#### etcd3

```go
viper.AddRemoteProvider("etcd3", "http://127.0.0.1:4001","/config/hugo.json")
viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
err := viper.ReadRemoteConfig()
```

#### Consul

Given a Consul key `MY_CONSUL_KEY` with the value:

```json
{
    "port": 8080,
    "hostname": "myhostname.com"
}
```

```go
viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY")
viper.SetConfigType("json") // Need to explicitly set this to json
err := viper.ReadRemoteConfig()

fmt.Println(viper.Get("port")) // 8080
```

#### Firestore

```go
viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document")
viper.SetConfigType("json") // Config's format: "json", "toml", "yaml", "yml"
err := viper.ReadRemoteConfig()
```

Of course, you're allowed to use `SecureRemoteProvider` also.

#### NATS

```go
viper.AddRemoteProvider("nats", "nats://127.0.0.1:4222", "myapp.config")
viper.SetConfigType("json")
err := viper.ReadRemoteConfig()
```

### Remote Key/Value Store Examples (Encrypted)

```go
viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg")
viper.SetConfigType("json") // because there is no file extension in a stream of bytes,  supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
err := viper.ReadRemoteConfig()
```

### Watching Key/Value Store Changes

```go
// Alternatively, you can create a new viper instance
var runtime_viper = viper.New()

runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml")
runtime_viper.SetConfigType("yaml") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"

// Read from remote config the first time
err := runtime_viper.ReadRemoteConfig()

// Unmarshal config
runtime_viper.Unmarshal(&runtime_conf)

// Open a goroutine to watch remote changes forever
go func(){
	for {
		time.Sleep(time.Second * 5) // delay after each request

		// Currently, only tested with Etcd support
		err := runtime_viper.WatchRemoteConfig()
		if err != nil {
			log.Errorf("unable to read remote config: %v", err)
			continue
		}

		// Unmarshal new config into our runtime config struct
		runtime_viper.Unmarshal(&runtime_conf)
	}
}()
```

## Getting Values From Viper

The simplest way to retrieve configuration values from Viper is to use `Get*`
functions. `Get` will return an any type, but specific types may be retrieved
with `Get<Type>` functions.

Note that each `Get*` function will return a zero value if it’s key is not
found. To check if a key exists, use the `IsSet` method.

Nested keys use `.` as a delimiter and numbers for array indexes. Given the
following configuration:

```jsonc
{
    "datastore": {
        "metric": {
            "host": "127.0.0.1",
            "ports": [
                5799,
                6029
            ]
        }
    }
}

```

```go
GetString("datastore.metric.host") // "127.0.0.1"
GetInt("host.ports.1") // 6029
```

> **NOTE** Viper _does not_ deep merge configuration values. Complex values
> that are overridden will be entirely replaced.

If there exists a key that matches the delimited key path, its value will be
returned instead.

```jsonc
{
    "datastore.metric.host": "0.0.0.0",
    "datastore": {
        "metric": {
            "host": "127.0.0.1"
        }
    }
}
```

```go
GetString("datastore.metric.host") // "0.0.0.0"
```

### Configuration Subsets

It's often useful to extract a subset of configuration (e.g., when developing a
reusable module which should accept specific sections of configuration).

```yaml
cache:
  cache1:
    item-size: 64
    max-items: 100
  cache2:
    item-size: 80
    max-items: 200
```

```go
func NewCache(v *Viper) *Cache {
	return &Cache{
		ItemSize: v.GetInt("item-size"),
		MaxItems: v.GetInt("max-items"),
	}
}

cache1Config := viper.Sub("cache.cache1")

if cache1Config == nil {
    // Sub returns nil if the key cannot be found
	panic("cache configuration not found")
}

cache1 := NewCache(cache1Config)
```

### Unmarshaling

You also have the option of unmarshaling configuration to a struct, map, etc.,
using `Unmarshal*` methods.

```go
type config struct {
	Port int
	Name string
	PathMap string `mapstructure:"path_map"`
}

var C config

err := viper.Unmarshal(&C)
if err != nil {
	t.Fatalf("unable to decode into struct, %v", err)
}
```

If you want to unmarshal configuration where the keys themselves contain `.`
(the default key delimiter), you can change the delimiter.

```go
v := viper.NewWithOptions(viper.KeyDelimiter("::"))

v.SetDefault("chart::values", map[string]any{
	"ingress": map[string]any{
		"annotations": map[string]any{
			"traefik.frontend.rule.type":                 "PathPrefix",
			"traefik.ingress.kubernetes.io/ssl-redirect": "true",
		},
	},
})

type config struct {
	Chart struct{
		Values map[string]any
	}
}

var C config

v.Unmarshal(&C)
```

Viper also supports unmarshaling into embedded structs.

```go
/*
Example config:

module:
    enabled: true
    token: 89h3f98hbwf987h3f98wenf89ehf
*/
type config struct {
	Module struct {
		Enabled bool

		moduleConfig `mapstructure:",squash"`
	}
}

type moduleConfig struct {
	Token string
}

var C config

err := viper.Unmarshal(&C)
if err != nil {
	t.Fatalf("unable to decode into struct, %v", err)
}
```

Viper uses
[github.com/go-viper/mapstructure](https://github.com/go-viper/mapstructure)
under the hood for unmarshaling values which uses `mapstructure` tags, by
default.

### Marshalling to String

You may need to marshal all the settings held in Viper into a string. You can
use your favorite format's marshaller with the config returned by
`AllSettings`.

```go
import (
	yaml "go.yaml.in/yaml/v3"
)

func yamlStringSettings() string {
	c := viper.AllSettings()
	bs, err := yaml.Marshal(c)
	if err != nil {
		log.Fatalf("unable to marshal config to YAML: %v", err)
	}
	return string(bs)
}
```

### Decoding Custom Formats

A frequently requested feature is adding more value formats and decoders (for
example; parsing character delimited strings into slices. This is already
available in Viper using mapstructure decode hooks.

Read more in [this blog
post](https://sagikazarmark.hu/blog/decoding-custom-formats-with-viper/).


## FAQ

### Why is it called “Viper”?

Viper is designed to be a
[companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe)) to
[Cobra](https://github.com/spf13/cobra). While both can operate completely
independently, together they make a powerful pair to handle much of your
application foundation needs.

### I found a bug or want a feature, should I file an issue or a PR?

Yes, but there are two things to be aware of.

1.  The Viper project is currently prioritizing backwards compatibility and
    stability over features.
2.  Features may be deferred until Viper 2 forms.

### Can multiple Viper instances be used?

**tl;dr:** Yes.

Each will have its own unique configuration and can read from a different
configuration source. All of the functions that the Viper package supports are
mirrored as methods on a Viper instance.

```go
x := viper.New()
y := viper.New()

x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")
```

### Should Viper be a global singleton or passed around?

The best practice is to initialize a Viper instance and pass that around when
necessary.

Viper comes with a global instance (singleton) out of the box. Although it
makes setting up configuration easy, using it is generally discouraged as it
makes testing harder and can lead to unexpected behavior.

The global instance may be deprecated in the future. See
[#1855](https://github.com/spf13/viper/issues/1855) for more details.

### Does Viper support case sensitive keys?

**tl;dr:** No.

Viper merges configuration from various sources, many of which are either case
insensitive or use different casing than other sources (e.g., env vars). In
order to provide the best experience when using multiple sources, all keys are
made case insensitive.

There has been several attempts to implement case sensitivity, but
unfortunately it's not trivial. We might take a stab at implementing it in
[Viper v2](https://github.com/spf13/viper/issues/772), but despite the initial
noise, it does not seem to be requested that much.

You can vote for case sensitivity by filling out this feedback form:
https://forms.gle/R6faU74qPRPAzchZ9.

### Is it safe to concurrently read and write to a Viper instance?

No, you will need to synchronize access to Viper yourself (for example by using
the `sync` package). Concurrent reads and writes can cause a panic.


## Troubleshooting

See [TROUBLESHOOTING.md](TROUBLESHOOTING.md).


## Development

**For an optimal developer experience, it is recommended to install
[Nix](https://nixos.org/download.html) and
[direnv](https://direnv.net/docs/installation.html).**

_Alternatively, install [Go](https://go.dev/dl/) on your computer then run
`make deps` to install the rest of the dependencies._

Run the test suite:

```shell
make test
```

Run linters:

```shell
make lint # pass -j option to run them in parallel
```

Some linter violations can automatically be fixed:

```shell
make fmt
```


## License

The project is licensed under the [MIT License](LICENSE).


================================================
FILE: TROUBLESHOOTING.md
================================================
# Troubleshooting

## Unmarshaling doesn't work

The most common reason for this issue is improper use of struct tags (eg. `yaml` or `json`). Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. Please refer to the library's documentation for using other struct tags.

## Cannot find package

Viper installation seems to fail a lot lately with the following (or a similar) error:

```
cannot find package "github.com/hashicorp/hcl/tree/hcl1" in any of:
/usr/local/Cellar/go/1.15.7_1/libexec/src/github.com/hashicorp/hcl/tree/hcl1 (from $GOROOT)
/Users/user/go/src/github.com/hashicorp/hcl/tree/hcl1 (from $GOPATH)
```

As the error message suggests, Go tries to look up dependencies in `GOPATH` mode (as it's commonly called) from the `GOPATH`.
Viper opted to use [Go Modules](https://go.dev/wiki/Modules) to manage its dependencies. While in many cases the two methods are interchangeable, once a dependency releases new (major) versions, `GOPATH` mode is no longer able to decide which version to use, so it'll either use one that's already present or pick a version (usually the `master` branch).

The solution is easy: switch to using Go Modules.
Please refer to the [wiki](https://go.dev/wiki/Modules) on how to do that.

**tl;dr* `export GO111MODULE=on`

## Unquoted 'y' and 'n' characters get replaced with _true_ and _false_ when reading a YAML file

This is a YAML 1.1 feature according to [go-yaml/yaml#740](https://github.com/go-yaml/yaml/issues/740).

Potential solutions are:

1. Quoting values resolved as boolean
1. Upgrading to YAML v3 (for the time being this is possible by passing the `viper_yaml3` tag to your build)


================================================
FILE: UPGRADE.md
================================================
# Update Log

**This document details any major updates required to use new features or improvements in Viper.**

## v1.20.x

### New file searching API

Viper now includes a new file searching API that allows users to customize how Viper looks for config files.

Viper accepts a custom [`Finder`](https://pkg.go.dev/github.com/spf13/viper#Finder) interface implementation:

```go
// Finder looks for files and directories in an [afero.Fs] filesystem.
type Finder interface {
	Find(fsys afero.Fs) ([]string, error)
}
```

It is supposed to return a list of paths to config files.

The default implementation uses [github.com/sagikazarmark/locafero](https://github.com/sagikazarmark/locafero) under the hood.

You can supply your own implementation using `WithFinder`:

```go
v := viper.NewWithOptions(
    viper.WithFinder(&MyFinder{}),
)
```

For more information, check out the [Finder examples](https://pkg.go.dev/github.com/spf13/viper#Finder)
and the [documentation](https://pkg.go.dev/github.com/sagikazarmark/locafero) for the locafero package.

### New encoding API

Viper now allows customizing the encoding layer by providing an API for encoding and decoding configuration data:

```go
// Encoder encodes Viper's internal data structures into a byte representation.
// It's primarily used for encoding a map[string]any into a file format.
type Encoder interface {
	Encode(v map[string]any) ([]byte, error)
}

// Decoder decodes the contents of a byte slice into Viper's internal data structures.
// It's primarily used for decoding contents of a file into a map[string]any.
type Decoder interface {
	Decode(b []byte, v map[string]any) error
}

// Codec combines [Encoder] and [Decoder] interfaces.
type Codec interface {
	Encoder
	Decoder
}
```

By default, Viper includes the following codecs:

- JSON
- TOML
- YAML
- Dotenv

The rest of the codecs are moved to [github.com/go-viper/encoding](https://github.com/go-viper/encoding)

Customizing the encoding layer is possible by providing a custom registry of codecs:

- [Encoder](https://pkg.go.dev/github.com/spf13/viper#Encoder) -> [EncoderRegistry](https://pkg.go.dev/github.com/spf13/viper#EncoderRegistry)
- [Decoder](https://pkg.go.dev/github.com/spf13/viper#Decoder) -> [DecoderRegistry](https://pkg.go.dev/github.com/spf13/viper#DecoderRegistry)
- [Codec](https://pkg.go.dev/github.com/spf13/viper#Codec) -> [CodecRegistry](https://pkg.go.dev/github.com/spf13/viper#CodecRegistry)

You can supply the registry of codecs to Viper using the appropriate `With*Registry` function:

```go
codecRegistry := viper.NewCodecRegistry()

codecRegistry.RegisterCodec("myformat", &MyCodec{})

v := viper.NewWithOptions(
    viper.WithCodecRegistry(codecRegistry),
)
```

### BREAKING: "github.com/mitchellh/mapstructure" depedency replaced

The original [mapstructure](https://github.com/mitchellh/mapstructure) has been [archived](https://github.com/mitchellh/mapstructure/issues/349) and was replaced with a [fork](https://github.com/go-viper/mapstructure) maintained by Viper ([#1723](https://github.com/spf13/viper/pull/1723)).

As a result, the package import path needs to be changed in cases where `mapstructure` is directly referenced in your code.

For example, when providing a custom decoder config:

```go
err := viper.Unmarshal(&appConfig, func(config *mapstructure.DecoderConfig) {
	config.TagName = "yaml"
})
```

The change is fairly straightforward, just replace all occurrences of the import path `github.com/mitchellh/mapstructure` with `github.com/go-viper/mapstructure/v2`:

```diff
- import "github.com/mitchellh/mapstructure"
+ import "github.com/go-viper/mapstructure/v2"
```

### BREAKING: HCL, Java properties, INI removed from core

In order to reduce third-party dependencies, Viper dropped support for the following formats from the core:

- HCL
- Java properties
- INI

You can still use these formats though by importing them from [github.com/go-viper/encoding](https://github.com/go-viper/encoding):

```go
import (
    "github.com/go-viper/encoding/hcl"
    "github.com/go-viper/encoding/javaproperties"
    "github.com/go-viper/encoding/ini"
)

codecRegistry := viper.NewCodecRegistry()

{
    codec := hcl.Codec{}

    codecRegistry.RegisterCodec("hcl", codec)
    codecRegistry.RegisterCodec("tfvars", codec)

}

{
    codec := &javaproperties.Codec{}

    codecRegistry.RegisterCodec("properties", codec)
    codecRegistry.RegisterCodec("props", codec)
    codecRegistry.RegisterCodec("prop", codec)
}

codecRegistry.RegisterCodec("ini", ini.Codec{})

v := viper.NewWithOptions(
    viper.WithCodecRegistry(codecRegistry),
)
```


================================================
FILE: encoding.go
================================================
package viper

import (
	"errors"
	"strings"
	"sync"

	"github.com/spf13/viper/internal/encoding/dotenv"
	"github.com/spf13/viper/internal/encoding/json"
	"github.com/spf13/viper/internal/encoding/toml"
	"github.com/spf13/viper/internal/encoding/yaml"
)

// Encoder encodes Viper's internal data structures into a byte representation.
// It's primarily used for encoding a map[string]any into a file format.
type Encoder interface {
	Encode(v map[string]any) ([]byte, error)
}

// Decoder decodes the contents of a byte slice into Viper's internal data structures.
// It's primarily used for decoding contents of a file into a map[string]any.
type Decoder interface {
	Decode(b []byte, v map[string]any) error
}

// Codec combines [Encoder] and [Decoder] interfaces.
type Codec interface {
	Encoder
	Decoder
}

// TODO: consider adding specific errors for not found scenarios

// EncoderRegistry returns an [Encoder] for a given format.
//
// Format is case-insensitive.
//
// [EncoderRegistry] returns an error if no [Encoder] is registered for the format.
type EncoderRegistry interface {
	Encoder(format string) (Encoder, error)
}

// DecoderRegistry returns an [Decoder] for a given format.
//
// Format is case-insensitive.
//
// [DecoderRegistry] returns an error if no [Decoder] is registered for the format.
type DecoderRegistry interface {
	Decoder(format string) (Decoder, error)
}

// CodecRegistry combines [EncoderRegistry] and [DecoderRegistry] interfaces.
type CodecRegistry interface {
	EncoderRegistry
	DecoderRegistry
}

// WithEncoderRegistry sets a custom [EncoderRegistry].
func WithEncoderRegistry(r EncoderRegistry) Option {
	return optionFunc(func(v *Viper) {
		if r == nil {
			return
		}

		v.encoderRegistry = r
	})
}

// WithDecoderRegistry sets a custom [DecoderRegistry].
func WithDecoderRegistry(r DecoderRegistry) Option {
	return optionFunc(func(v *Viper) {
		if r == nil {
			return
		}

		v.decoderRegistry = r
	})
}

// WithCodecRegistry sets a custom [EncoderRegistry] and [DecoderRegistry].
func WithCodecRegistry(r CodecRegistry) Option {
	return optionFunc(func(v *Viper) {
		if r == nil {
			return
		}

		v.encoderRegistry = r
		v.decoderRegistry = r
	})
}

// DefaultCodecRegistry is a simple implementation of [CodecRegistry] that allows registering custom [Codec]s.
type DefaultCodecRegistry struct {
	codecs map[string]Codec

	mu   sync.RWMutex
	once sync.Once
}

// NewCodecRegistry returns a new [CodecRegistry], ready to accept custom [Codec]s.
func NewCodecRegistry() *DefaultCodecRegistry {
	r := &DefaultCodecRegistry{}

	r.init()

	return r
}

func (r *DefaultCodecRegistry) init() {
	r.once.Do(func() {
		r.codecs = map[string]Codec{}
	})
}

// RegisterCodec registers a custom [Codec].
//
// Format is case-insensitive.
func (r *DefaultCodecRegistry) RegisterCodec(format string, codec Codec) error {
	r.init()

	r.mu.Lock()
	defer r.mu.Unlock()

	r.codecs[strings.ToLower(format)] = codec

	return nil
}

// Encoder implements the [EncoderRegistry] interface.
//
// Format is case-insensitive.
func (r *DefaultCodecRegistry) Encoder(format string) (Encoder, error) {
	encoder, ok := r.codec(format)
	if !ok {
		return nil, errors.New("encoder not found for this format")
	}

	return encoder, nil
}

// Decoder implements the [DecoderRegistry] interface.
//
// Format is case-insensitive.
func (r *DefaultCodecRegistry) Decoder(format string) (Decoder, error) {
	decoder, ok := r.codec(format)
	if !ok {
		return nil, errors.New("decoder not found for this format")
	}

	return decoder, nil
}

func (r *DefaultCodecRegistry) codec(format string) (Codec, bool) {
	r.mu.Lock()
	defer r.mu.Unlock()

	format = strings.ToLower(format)

	if r.codecs != nil {
		codec, ok := r.codecs[format]
		if ok {
			return codec, true
		}
	}

	switch format {
	case "yaml", "yml":
		return yaml.Codec{}, true

	case "json":
		return json.Codec{}, true

	case "toml":
		return toml.Codec{}, true

	case "dotenv", "env":
		return &dotenv.Codec{}, true
	}

	return nil, false
}


================================================
FILE: encoding_test.go
================================================
package viper

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type codec struct{}

func (codec) Encode(_ map[string]any) ([]byte, error) {
	return nil, nil
}

func (codec) Decode(_ []byte, _ map[string]any) error {
	return nil
}

func TestDefaultCodecRegistry(t *testing.T) {
	t.Run("OK", func(t *testing.T) {
		registry := NewCodecRegistry()

		c := codec{}

		err := registry.RegisterCodec("myformat", c)
		require.NoError(t, err)

		encoder, err := registry.Encoder("myformat")
		require.NoError(t, err)

		assert.Equal(t, c, encoder)

		decoder, err := registry.Decoder("myformat")
		require.NoError(t, err)

		assert.Equal(t, c, decoder)
	})

	t.Run("CodecNotFound", func(t *testing.T) {
		registry := NewCodecRegistry()

		_, err := registry.Encoder("myformat")
		require.Error(t, err)

		_, err = registry.Decoder("myformat")
		require.Error(t, err)
	})

	t.Run("FormatIsCaseInsensitive", func(t *testing.T) {
		registry := NewCodecRegistry()

		c := codec{}

		err := registry.RegisterCodec("MYFORMAT", c)
		require.NoError(t, err)

		{
			encoder, err := registry.Encoder("myformat")
			require.NoError(t, err)

			assert.Equal(t, c, encoder)
		}

		{
			encoder, err := registry.Encoder("MYFORMAT")
			require.NoError(t, err)

			assert.Equal(t, c, encoder)
		}

		{
			decoder, err := registry.Decoder("myformat")
			require.NoError(t, err)

			assert.Equal(t, c, decoder)
		}

		{
			decoder, err := registry.Decoder("MYFORMAT")
			require.NoError(t, err)

			assert.Equal(t, c, decoder)
		}
	})

	t.Run("OverrideDefault", func(t *testing.T) {
		registry := NewCodecRegistry()

		c := codec{}

		err := registry.RegisterCodec("yaml", c)
		require.NoError(t, err)

		encoder, err := registry.Encoder("yaml")
		require.NoError(t, err)

		assert.Equal(t, c, encoder)

		decoder, err := registry.Decoder("yaml")
		require.NoError(t, err)

		assert.Equal(t, c, decoder)
	})
}


================================================
FILE: errors.go
================================================
package viper

import (
	"fmt"
)

// FileLookupError is returned when Viper cannot resolve a configuration file.
//
// This is meant to be a common interface for all file look-up errors, occurring either because a
// file does not exist or because it cannot find any file matching finder criteria.
type FileLookupError interface {
	error

	fileLookup()
}

// ConfigFileNotFoundError denotes failing to find a configuration file from a search.
//
// Deprecated: This is error wraps [FileNotFoundFromSearchError], which should be used instead.
type ConfigFileNotFoundError struct {
	locations []string
	name      string
}

// Error returns the formatted error.
func (e ConfigFileNotFoundError) Error() string {
	return e.Unwrap().Error()
}

// Unwraps to FileNotFoundFromSearchError.
func (e ConfigFileNotFoundError) Unwrap() error {
	return FileNotFoundFromSearchError(e)
}

// FileNotFoundFromSearchError denotes failing to find a configuration file from a search.
// Wraps ConfigFileNotFoundError.
type FileNotFoundFromSearchError struct {
	locations []string
	name      string
}

func (e FileNotFoundFromSearchError) fileLookup() {}

// Error returns the formatted error.
func (e FileNotFoundFromSearchError) Error() string {
	message := fmt.Sprintf("File %q not found", e.name)

	if len(e.locations) > 0 {
		message += fmt.Sprintf(" in %v", e.locations)
	}

	return message
}

// FileNotFoundError denotes failing to find a specific configuration file.
type FileNotFoundError struct {
	err  error
	path string
}

func (e FileNotFoundError) fileLookup() {}

// Error returns the formatted error.
func (e FileNotFoundError) Error() string {
	return fmt.Sprintf("file not found: %s", e.path)
}

// ConfigFileAlreadyExistsError denotes failure to write new configuration file.
type ConfigFileAlreadyExistsError string

// Error returns the formatted error when configuration already exists.
func (e ConfigFileAlreadyExistsError) Error() string {
	return fmt.Sprintf("Config File %q Already Exists", string(e))
}

// ConfigMarshalError happens when failing to marshal the configuration.
type ConfigMarshalError struct {
	err error
}

// Error returns the formatted configuration error.
func (e ConfigMarshalError) Error() string {
	return fmt.Sprintf("While marshaling config: %s", e.err.Error())
}

// UnsupportedConfigError denotes encountering an unsupported
// configuration filetype.
type UnsupportedConfigError string

// Error returns the formatted configuration error.
func (str UnsupportedConfigError) Error() string {
	return fmt.Sprintf("Unsupported Config Type %q", string(str))
}


================================================
FILE: experimental.go
================================================
package viper

// ExperimentalBindStruct tells Viper to use the new bind struct feature.
func ExperimentalBindStruct() Option {
	return optionFunc(func(v *Viper) {
		v.experimentalBindStruct = true
	})
}


================================================
FILE: file.go
================================================
package viper

import (
	"os"
	"path/filepath"

	"github.com/sagikazarmark/locafero"
	"github.com/spf13/afero"
)

// ExperimentalFinder tells Viper to use the new Finder interface for finding configuration files.
func ExperimentalFinder() Option {
	return optionFunc(func(v *Viper) {
		v.experimentalFinder = true
	})
}

// Search for a config file.
func (v *Viper) findConfigFile() (string, error) {
	finder := v.finder

	if finder == nil && v.experimentalFinder {
		var names []string

		if v.configType != "" {
			names = locafero.NameWithOptionalExtensions(v.configName, SupportedExts...)
		} else {
			names = locafero.NameWithExtensions(v.configName, SupportedExts...)
		}

		finder = locafero.Finder{
			Paths: v.configPaths,
			Names: names,
			Type:  locafero.FileTypeFile,
		}
	}

	if finder != nil {
		return v.findConfigFileWithFinder(finder)
	}

	return v.findConfigFileOld()
}

func (v *Viper) findConfigFileWithFinder(finder Finder) (string, error) {
	results, err := finder.Find(v.fs)
	if err != nil {
		return "", err
	}

	if len(results) == 0 {
		return "", ConfigFileNotFoundError{name: v.configName, locations: v.configPaths}
	}

	// We call clean on the final result to ensure that the path is in its canonical form.
	// This is mostly for consistent path handling and to make sure tests pass.
	return results[0], nil
}

// Search all configPaths for any config file.
// Returns the first path that exists (and is a config file).
func (v *Viper) findConfigFileOld() (string, error) {
	v.logger.Info("searching for config in paths", "paths", v.configPaths)

	for _, cp := range v.configPaths {
		file := v.searchInPath(cp)
		if file != "" {
			return file, nil
		}
	}
	return "", ConfigFileNotFoundError{name: v.configName, locations: v.configPaths}
}

func (v *Viper) searchInPath(in string) (filename string) {
	v.logger.Debug("searching for config in path", "path", in)
	for _, ext := range SupportedExts {
		v.logger.Debug("checking if file exists", "file", filepath.Join(in, v.configName+"."+ext))
		if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b {
			v.logger.Debug("found file", "file", filepath.Join(in, v.configName+"."+ext))
			return filepath.Join(in, v.configName+"."+ext)
		}
	}

	if v.configType != "" {
		if b, _ := exists(v.fs, filepath.Join(in, v.configName)); b {
			return filepath.Join(in, v.configName)
		}
	}

	return ""
}

// exists checks if file exists.
func exists(fs afero.Fs, path string) (bool, error) {
	stat, err := fs.Stat(path)
	if err == nil {
		return !stat.IsDir(), nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}


================================================
FILE: finder.go
================================================
package viper

import (
	"errors"

	"github.com/spf13/afero"
)

// WithFinder sets a custom [Finder].
func WithFinder(f Finder) Option {
	return optionFunc(func(v *Viper) {
		if f == nil {
			return
		}

		v.finder = f
	})
}

// Finder looks for files and directories in an [afero.Fs] filesystem.
type Finder interface {
	Find(fsys afero.Fs) ([]string, error)
}

// Finders combines multiple finders into one.
func Finders(finders ...Finder) Finder {
	return &combinedFinder{finders: finders}
}

// combinedFinder is a Finder that combines multiple finders.
type combinedFinder struct {
	finders []Finder
}

// Find implements the [Finder] interface.
func (c *combinedFinder) Find(fsys afero.Fs) ([]string, error) {
	var results []string
	var errs []error

	for _, finder := range c.finders {
		if finder == nil {
			continue
		}

		r, err := finder.Find(fsys)
		if err != nil {
			errs = append(errs, err)
			continue
		}

		results = append(results, r...)
	}

	return results, errors.Join(errs...)
}


================================================
FILE: finder_example_test.go
================================================
package viper_test

import (
	"fmt"

	"github.com/sagikazarmark/locafero"
	"github.com/spf13/afero"

	"github.com/spf13/viper"
)

func ExampleFinder() {
	fs := afero.NewMemMapFs()

	fs.Mkdir("/home/user", 0o777)

	f, _ := fs.Create("/home/user/myapp.yaml")
	f.WriteString("foo: bar")
	f.Close()

	// HCL will have a "lower" priority in the search order
	fs.Create("/home/user/myapp.hcl")

	finder := locafero.Finder{
		Paths: []string{"/home/user"},
		Names: locafero.NameWithExtensions("myapp", viper.SupportedExts...),
		Type:  locafero.FileTypeFile, // This is important!
	}

	v := viper.NewWithOptions(viper.WithFinder(finder))
	v.SetFs(fs)
	v.ReadInConfig()

	fmt.Println(v.GetString("foo"))

	// Output:
	// bar
}

func ExampleFinders() {
	fs := afero.NewMemMapFs()

	fs.Mkdir("/home/user", 0o777)
	f, _ := fs.Create("/home/user/myapp.yaml")
	f.WriteString("foo: bar")
	f.Close()

	fs.Mkdir("/etc/myapp", 0o777)
	fs.Create("/etc/myapp/config.yaml")

	// Combine multiple finders to search for files in multiple locations with different criteria
	finder := viper.Finders(
		locafero.Finder{
			Paths: []string{"/home/user"},
			Names: locafero.NameWithExtensions("myapp", viper.SupportedExts...),
			Type:  locafero.FileTypeFile, // This is important!
		},
		locafero.Finder{
			Paths: []string{"/etc/myapp"},
			Names: []string{"config.yaml"}, // Only accept YAML files in the system config directory
			Type:  locafero.FileTypeFile,   // This is important!
		},
	)

	v := viper.NewWithOptions(viper.WithFinder(finder))
	v.SetFs(fs)
	v.ReadInConfig()

	fmt.Println(v.GetString("foo"))

	// Output:
	// bar
}


================================================
FILE: finder_test.go
================================================
package viper

import (
	"testing"

	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type finderStub struct {
	results []string
}

func (f finderStub) Find(_ afero.Fs) ([]string, error) {
	return f.results, nil
}

func TestFinders(t *testing.T) {
	finder := Finders(
		finderStub{
			results: []string{
				"/home/user/.viper.yaml",
			},
		},
		finderStub{
			results: []string{
				"/etc/viper/config.yaml",
			},
		},
	)

	results, err := finder.Find(afero.NewMemMapFs())
	require.NoError(t, err)

	expected := []string{
		"/home/user/.viper.yaml",
		"/etc/viper/config.yaml",
	}

	assert.Equal(t, expected, results)
}


================================================
FILE: flags.go
================================================
package viper

import "github.com/spf13/pflag"

// FlagValueSet is an interface that users can implement
// to bind a set of flags to viper.
type FlagValueSet interface {
	VisitAll(fn func(FlagValue))
}

// FlagValue is an interface that users can implement
// to bind different flags to viper.
type FlagValue interface {
	HasChanged() bool
	Name() string
	ValueString() string
	ValueType() string
}

// pflagValueSet is a wrapper around *pflag.ValueSet
// that implements FlagValueSet.
type pflagValueSet struct {
	flags *pflag.FlagSet
}

// VisitAll iterates over all *pflag.Flag inside the *pflag.FlagSet.
func (p pflagValueSet) VisitAll(fn func(flag FlagValue)) {
	p.flags.VisitAll(func(flag *pflag.Flag) {
		fn(pflagValue{flag})
	})
}

// pflagValue is a wrapper around *pflag.flag
// that implements FlagValue.
type pflagValue struct {
	flag *pflag.Flag
}

// HasChanged returns whether the flag has changes or not.
func (p pflagValue) HasChanged() bool {
	return p.flag.Changed
}

// Name returns the name of the flag.
func (p pflagValue) Name() string {
	return p.flag.Name
}

// ValueString returns the value of the flag as a string.
func (p pflagValue) ValueString() string {
	return p.flag.Value.String()
}

// ValueType returns the type of the flag as a string.
func (p pflagValue) ValueType() string {
	return p.flag.Value.Type()
}


================================================
FILE: flags_test.go
================================================
package viper

import (
	"testing"

	"github.com/spf13/pflag"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestBindFlagValueSet(t *testing.T) {
	Reset()
	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)

	testValues := map[string]*string{
		"host":     nil,
		"port":     nil,
		"endpoint": nil,
	}

	mutatedTestValues := map[string]string{
		"host":     "localhost",
		"port":     "6060",
		"endpoint": "/public",
	}

	for name := range testValues {
		testValues[name] = flagSet.String(name, "", "test")
	}

	flagValueSet := pflagValueSet{flagSet}

	err := BindFlagValues(flagValueSet)
	require.NoError(t, err, "error binding flag set")

	flagSet.VisitAll(func(flag *pflag.Flag) {
		flag.Value.Set(mutatedTestValues[flag.Name])
		flag.Changed = true
	})

	for name, expected := range mutatedTestValues {
		assert.Equal(t, expected, Get(name))
	}
}

func TestBindFlagValue(t *testing.T) {
	testString := "testing"
	testValue := newStringValue(testString, &testString)

	flag := &pflag.Flag{
		Name:    "testflag",
		Value:   testValue,
		Changed: false,
	}

	flagValue := pflagValue{flag}
	BindFlagValue("testvalue", flagValue)

	assert.Equal(t, testString, Get("testvalue"))

	flag.Value.Set("testing_mutate")
	flag.Changed = true // hack for pflag usage

	assert.Equal(t, "testing_mutate", Get("testvalue"))
}


================================================
FILE: flake.nix
================================================
{
  description = "Viper";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    flake-parts.url = "github:hercules-ci/flake-parts";
    devenv.url = "github:cachix/devenv";
  };

  outputs =
    inputs@{ flake-parts, ... }:
    flake-parts.lib.mkFlake { inherit inputs; } {
      imports = [
        inputs.devenv.flakeModule
      ];

      systems = [
        "x86_64-linux"
        "x86_64-darwin"
        "aarch64-darwin"
      ];

      perSystem =
        { pkgs, ... }:
        {
          devenv.shells = {
            default = {
              languages = {
                go.enable = true;
              };

              git-hooks.hooks = {
                nixpkgs-fmt.enable = true;
                yamllint.enable = true;
              };

              packages = with pkgs; [
                gnumake

                golangci-lint
                yamllint
              ];

              scripts = {
                versions.exec = ''
                  go version
                  golangci-lint version
                '';
              };

              enterShell = ''
                versions
              '';

              # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
              containers = pkgs.lib.mkForce { };
            };
          };
        };
    };
}


================================================
FILE: go.mod
================================================
module github.com/spf13/viper

go 1.23.0

require (
	github.com/fsnotify/fsnotify v1.9.0
	github.com/go-viper/mapstructure/v2 v2.4.0
	github.com/pelletier/go-toml/v2 v2.2.4
	github.com/sagikazarmark/locafero v0.12.0
	github.com/spf13/afero v1.15.0
	github.com/spf13/cast v1.10.0
	github.com/spf13/pflag v1.0.10
	github.com/stretchr/testify v1.11.1
	github.com/subosito/gotenv v1.6.0
	go.yaml.in/yaml/v3 v3.0.4
)

require (
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/google/go-cmp v0.6.0 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	golang.org/x/sys v0.29.0 // indirect
	golang.org/x/text v0.28.0 // indirect
	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)


================================================
FILE: go.sum
================================================
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


================================================
FILE: internal/encoding/dotenv/codec.go
================================================
package dotenv

import (
	"bytes"
	"fmt"
	"sort"
	"strings"

	"github.com/subosito/gotenv"
)

const keyDelimiter = "_"

// Codec implements the encoding.Encoder and encoding.Decoder interfaces for encoding data containing environment variables
// (commonly called as dotenv format).
type Codec struct{}

// Encode encodes a map[string]any into a dotenv byte slice.
func (Codec) Encode(v map[string]any) ([]byte, error) {
	flattened := map[string]any{}

	flattened = flattenAndMergeMap(flattened, v, "", keyDelimiter)

	keys := make([]string, 0, len(flattened))

	for key := range flattened {
		keys = append(keys, key)
	}

	sort.Strings(keys)

	var buf bytes.Buffer

	for _, key := range keys {
		_, err := buf.WriteString(fmt.Sprintf("%v=%v\n", strings.ToUpper(key), flattened[key]))
		if err != nil {
			return nil, err
		}
	}

	return buf.Bytes(), nil
}

// Decode decodes a dotenv byte slice into a map[string]any.
func (Codec) Decode(b []byte, v map[string]any) error {
	var buf bytes.Buffer

	_, err := buf.Write(b)
	if err != nil {
		return err
	}

	env, err := gotenv.StrictParse(&buf)
	if err != nil {
		return err
	}

	for key, value := range env {
		v[key] = value
	}

	return nil
}


================================================
FILE: internal/encoding/dotenv/codec_test.go
================================================
package dotenv

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// original form of the data.
const original = `# key-value pair
KEY=value
`

// encoded form of the data.
const encoded = `KEY=value
`

// data is Viper's internal representation.
var data = map[string]any{
	"KEY": "value",
}

func TestCodec_Encode(t *testing.T) {
	codec := Codec{}

	b, err := codec.Encode(data)
	require.NoError(t, err)

	assert.Equal(t, encoded, string(b))
}

func TestCodec_Decode(t *testing.T) {
	t.Run("OK", func(t *testing.T) {
		codec := Codec{}

		v := map[string]any{}

		err := codec.Decode([]byte(original), v)
		require.NoError(t, err)

		assert.Equal(t, data, v)
	})

	t.Run("InvalidData", func(t *testing.T) {
		codec := Codec{}

		v := map[string]any{}

		err := codec.Decode([]byte(`invalid data`), v)
		require.Error(t, err)

		t.Logf("decoding failed as expected: %s", err)
	})
}


================================================
FILE: internal/encoding/dotenv/map_utils.go
================================================
package dotenv

import (
	"strings"

	"github.com/spf13/cast"
)

// flattenAndMergeMap recursively flattens the given map into a new map
// Code is based on the function with the same name in the main package.
// TODO: move it to a common place.
func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any {
	if shadow != nil && prefix != "" && shadow[prefix] != nil {
		// prefix is shadowed => nothing more to flatten
		return shadow
	}
	if shadow == nil {
		shadow = make(map[string]any)
	}

	var m2 map[string]any
	if prefix != "" {
		prefix += delimiter
	}
	for k, val := range m {
		fullKey := prefix + k
		switch val := val.(type) {
		case map[string]any:
			m2 = val
		case map[any]any:
			m2 = cast.ToStringMap(val)
		default:
			// immediate value
			shadow[strings.ToLower(fullKey)] = val
			continue
		}
		// recursively merge to shadow map
		shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)
	}
	return shadow
}


================================================
FILE: internal/encoding/json/codec.go
================================================
package json

import (
	"encoding/json"
)

// Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding.
type Codec struct{}

// Encode encodes a map[string]any into a JSON byte slice.
func (Codec) Encode(v map[string]any) ([]byte, error) {
	// TODO: expose prefix and indent in the Codec as setting?
	return json.MarshalIndent(v, "", "  ")
}

// Decode decodes a JSON byte slice into a map[string]any.
func (Codec) Decode(b []byte, v map[string]any) error {
	return json.Unmarshal(b, &v)
}


================================================
FILE: internal/encoding/json/codec_test.go
================================================
package json

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// encoded form of the data.
const encoded = `{
  "key": "value",
  "list": [
    "item1",
    "item2",
    "item3"
  ],
  "map": {
    "key": "value"
  },
  "nested_map": {
    "map": {
      "key": "value",
      "list": [
        "item1",
        "item2",
        "item3"
      ]
    }
  }
}`

// data is Viper's internal representation.
var data = map[string]any{
	"key": "value",
	"list": []any{
		"item1",
		"item2",
		"item3",
	},
	"map": map[string]any{
		"key": "value",
	},
	"nested_map": map[string]any{
		"map": map[string]any{
			"key": "value",
			"list": []any{
				"item1",
				"item2",
				"item3",
			},
		},
	},
}

func TestCodec_Encode(t *testing.T) {
	codec := Codec{}

	b, err := codec.Encode(data)
	require.NoError(t, err)

	assert.JSONEq(t, encoded, string(b))
}

func TestCodec_Decode(t *testing.T) {
	t.Run("OK", func(t *testing.T) {
		codec := Codec{}

		v := map[string]any{}

		err := codec.Decode([]byte(encoded), v)
		require.NoError(t, err)

		assert.Equal(t, data, v)
	})

	t.Run("InvalidData", func(t *testing.T) {
		codec := Codec{}

		v := map[string]any{}

		err := codec.Decode([]byte(`invalid data`), v)
		require.Error(t, err)

		t.Logf("decoding failed as expected: %s", err)
	})
}


================================================
FILE: internal/encoding/toml/codec.go
================================================
package toml

import (
	"github.com/pelletier/go-toml/v2"
)

// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
type Codec struct{}

// Encode encodes a map[string]any into a TOML byte slice.
func (Codec) Encode(v map[string]any) ([]byte, error) {
	return toml.Marshal(v)
}

// Decode decodes a TOML byte slice into a map[string]any.
func (Codec) Decode(b []byte, v map[string]any) error {
	return toml.Unmarshal(b, &v)
}


================================================
FILE: internal/encoding/toml/codec_test.go
================================================
package toml

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// original form of the data.
const original = `# key-value pair
key = "value"
list = ["item1", "item2", "item3"]

[map]
key = "value"

# nested
# map
[nested_map]
[nested_map.map]
key = "value"
list = [
  "item1",
  "item2",
  "item3",
]
`

// encoded form of the data.
const encoded = `key = 'value'
list = ['item1', 'item2', 'item3']

[map]
key = 'value'

[nested_map]
[nested_map.map]
key = 'value'
list = ['item1', 'item2', 'item3']
`

// data is Viper's internal representation.
var data = map[string]any{
	"key": "value",
	"list": []any{
		"item1",
		"item2",
		"item3",
	},
	"map": map[string]any{
		"key": "value",
	},
	"nested_map": map[string]any{
		"map": map[string]any{
			"key": "value",
			"list": []any{
				"item1",
				"item2",
				"item3",
			},
		},
	},
}

func TestCodec_Encode(t *testing.T) {
	codec := Codec{}

	b, err := codec.Encode(data)
	require.NoError(t, err)

	assert.Equal(t, encoded, string(b))
}

func TestCodec_Decode(t *testing.T) {
	t.Run("OK", func(t *testing.T) {
		codec := Codec{}

		v := map[string]any{}

		err := codec.Decode([]byte(original), v)
		require.NoError(t, err)

		assert.Equal(t, data, v)
	})

	t.Run("InvalidData", func(t *testing.T) {
		codec := Codec{}

		v := map[string]any{}

		err := codec.Decode([]byte(`invalid data`), v)
		require.Error(t, err)

		t.Logf("decoding failed as expected: %s", err)
	})
}


================================================
FILE: internal/encoding/yaml/codec.go
================================================
package yaml

import "go.yaml.in/yaml/v3"

// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
type Codec struct{}

// Encode encodes a map[string]any into a YAML byte slice.
func (Codec) Encode(v map[string]any) ([]byte, error) {
	return yaml.Marshal(v)
}

// Decode decodes a YAML byte slice into a map[string]any.
func (Codec) Decode(b []byte, v map[string]any) error {
	return yaml.Unmarshal(b, &v)
}


================================================
FILE: internal/encoding/yaml/codec_test.go
================================================
package yaml

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// original form of the data.
const original = `# key-value pair
key: value
list:
    - item1
    - item2
    - item3
map:
    key: value

# nested
# map
nested_map:
    map:
        key: value
        list:
            - item1
            - item2
            - item3
`

// encoded form of the data.
const encoded = `key: value
list:
    - item1
    - item2
    - item3
map:
    key: value
nested_map:
    map:
        key: value
        list:
            - item1
            - item2
            - item3
`

// decoded form of the data.
//
// In case of YAML it's slightly different from Viper's internal representation
// (e.g. map is decoded into a map with interface key).
var decoded = map[string]any{
	"key": "value",
	"list": []any{
		"item1",
		"item2",
		"item3",
	},
	"map": map[string]any{
		"key": "value",
	},
	"nested_map": map[string]any{
		"map": map[string]any{
			"key": "value",
			"list": []any{
				"item1",
				"item2",
				"item3",
			},
		},
	},
}

// data is Viper's internal representation.
var data = map[string]any{
	"key": "value",
	"list": []any{
		"item1",
		"item2",
		"item3",
	},
	"map": map[string]any{
		"key": "value",
	},
	"nested_map": map[string]any{
		"map": map[string]any{
			"key": "value",
			"list": []any{
				"item1",
				"item2",
				"item3",
			},
		},
	},
}

func TestCodec_Encode(t *testing.T) {
	codec := Codec{}

	b, err := codec.Encode(data)
	require.NoError(t, err)

	assert.Equal(t, encoded, string(b))
}

func TestCodec_Decode(t *testing.T) {
	t.Run("OK", func(t *testing.T) {
		codec := Codec{}

		v := map[string]any{}

		err := codec.Decode([]byte(original), v)
		require.NoError(t, err)

		assert.Equal(t, decoded, v)
	})

	t.Run("InvalidData", func(t *testing.T) {
		codec := Codec{}

		v := map[string]any{}

		err := codec.Decode([]byte(`invalid data`), v)
		require.Error(t, err)

		t.Logf("decoding failed as expected: %s", err)
	})
}


================================================
FILE: internal/features/bind_struct.go
================================================
//go:build viper_bind_struct

package features

// BindStruct is a feature flag for enabling/disabling the config binding to structs.
const BindStruct = true


================================================
FILE: internal/features/bind_struct_default.go
================================================
//go:build !viper_bind_struct

package features

// BindStruct is a feature flag for enabling/disabling the config binding to structs.
const BindStruct = false


================================================
FILE: internal/features/finder.go
================================================
//go:build viper_finder

package features

// Finder is a feature flag for enabling/disabling the config finder.
const Finder = true


================================================
FILE: internal/features/finder_default.go
================================================
//go:build !viper_finder

package features

// Finder is a feature flag for enabling/disabling the config finder.
const Finder = false


================================================
FILE: internal/testutil/filepath.go
================================================
package testutil

import (
	"path/filepath"
	"testing"
)

// AbsFilePath calls filepath.Abs on path.
func AbsFilePath(t *testing.T, path string) string {
	t.Helper()

	s, err := filepath.Abs(path)
	if err != nil {
		t.Fatal(err)
	}

	return s
}


================================================
FILE: logger.go
================================================
package viper

import (
	"context"
	"log/slog"
)

// WithLogger sets a custom logger.
func WithLogger(l *slog.Logger) Option {
	return optionFunc(func(v *Viper) {
		v.logger = l
	})
}

type discardHandler struct{}

func (n *discardHandler) Enabled(_ context.Context, _ slog.Level) bool {
	return false
}

func (n *discardHandler) Handle(_ context.Context, _ slog.Record) error {
	return nil
}

func (n *discardHandler) WithAttrs(_ []slog.Attr) slog.Handler {
	return n
}

func (n *discardHandler) WithGroup(_ string) slog.Handler {
	return n
}


================================================
FILE: overrides_test.go
================================================
package viper

import (
	"strings"
	"testing"

	"github.com/spf13/cast"
	"github.com/stretchr/testify/assert"
)

type layer int

const (
	defaultLayer layer = iota + 1
	overrideLayer
)

func TestNestedOverrides(t *testing.T) {
	assert := assert.New(t)
	var v *Viper

	// Case 0: value overridden by a value
	overrideDefault(assert, "tom", 10, "tom", 20) // "tom" is first given 10 as default value, then overridden by 20
	override(assert, "tom", 10, "tom", 20)        // "tom" is first given value 10, then overridden by 20
	overrideDefault(assert, "tom.age", 10, "tom.age", 20)
	override(assert, "tom.age", 10, "tom.age", 20)
	overrideDefault(assert, "sawyer.tom.age", 10, "sawyer.tom.age", 20)
	override(assert, "sawyer.tom.age", 10, "sawyer.tom.age", 20)

	// Case 1: key:value overridden by a value
	v = overrideDefault(assert, "tom.age", 10, "tom", "boy") // "tom.age" is first given 10 as default value, then "tom" is overridden by "boy"
	assert.Nil(v.Get("tom.age"))                             // "tom.age" should not exist anymore
	v = override(assert, "tom.age", 10, "tom", "boy")
	assert.Nil(v.Get("tom.age"))

	// Case 2: value overridden by a key:value
	overrideDefault(assert, "tom", "boy", "tom.age", 10) // "tom" is first given "boy" as default value, then "tom" is overridden by map{"age":10}
	override(assert, "tom.age", 10, "tom", "boy")

	// Case 3: key:value overridden by a key:value
	v = overrideDefault(assert, "tom.size", 4, "tom.age", 10)
	assert.Equal(4, v.Get("tom.size")) // value should still be reachable
	v = override(assert, "tom.size", 4, "tom.age", 10)
	assert.Equal(4, v.Get("tom.size"))
	deepCheckValue(assert, v, overrideLayer, []string{"tom", "size"}, 4)

	// Case 4: key:value overridden by a map
	v = overrideDefault(assert, "tom.size", 4, "tom", map[string]any{"age": 10}) // "tom.size" is first given "4" as default value, then "tom" is overridden by map{"age":10}
	assert.Equal(4, v.Get("tom.size"))                                           // "tom.size" should still be reachable
	assert.Equal(10, v.Get("tom.age"))                                           // new value should be there
	deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10)         // new value should be there
	v = override(assert, "tom.size", 4, "tom", map[string]any{"age": 10})
	assert.Nil(v.Get("tom.size"))
	assert.Equal(10, v.Get("tom.age"))
	deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10)

	// Case 5: array overridden by a value
	overrideDefault(assert, "tom", []int{10, 20}, "tom", 30)
	override(assert, "tom", []int{10, 20}, "tom", 30)
	overrideDefault(assert, "tom.age", []int{10, 20}, "tom.age", 30)
	override(assert, "tom.age", []int{10, 20}, "tom.age", 30)

	// Case 6: array overridden by an array
	overrideDefault(assert, "tom", []int{10, 20}, "tom", []int{30, 40})
	override(assert, "tom", []int{10, 20}, "tom", []int{30, 40})
	overrideDefault(assert, "tom.age", []int{10, 20}, "tom.age", []int{30, 40})
	v = override(assert, "tom.age", []int{10, 20}, "tom.age", []int{30, 40})
	// explicit array merge:
	s, ok := v.Get("tom.age").([]int)
	if assert.True(ok, "tom[\"age\"] is not a slice") {
		v.Set("tom.age", append(s, []int{50, 60}...))
		assert.Equal([]int{30, 40, 50, 60}, v.Get("tom.age"))
		deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, []int{30, 40, 50, 60})
	}
}

func overrideDefault(assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {
	return overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue)
}

func override(assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {
	return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue)
}

// overrideFromLayer performs the sequential override and low-level checks.
//
// First assignment is made on layer l for path firstPath with value firstValue,
// the second one on the override layer (i.e., with the Set() function)
// for path secondPath with value secondValue.
//
// firstPath and secondPath can include an arbitrary number of dots to indicate
// a nested element.
//
// After each assignment, the value is checked, retrieved both by its full path
// and by its key sequence (successive maps).
func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {
	v := New()
	firstKeys := strings.Split(firstPath, v.keyDelim)
	if assert == nil ||
		len(firstKeys) == 0 || firstKeys[0] == "" {
		return v
	}

	// Set and check first value
	switch l {
	case defaultLayer:
		v.SetDefault(firstPath, firstValue)
	case overrideLayer:
		v.Set(firstPath, firstValue)
	default:
		return v
	}
	assert.Equal(firstValue, v.Get(firstPath))
	deepCheckValue(assert, v, l, firstKeys, firstValue)

	// Override and check new value
	secondKeys := strings.Split(secondPath, v.keyDelim)
	if len(secondKeys) == 0 || secondKeys[0] == "" {
		return v
	}
	v.Set(secondPath, secondValue)
	assert.Equal(secondValue, v.Get(secondPath))
	deepCheckValue(assert, v, overrideLayer, secondKeys, secondValue)

	return v
}

// deepCheckValue checks that all given keys correspond to a valid path in the
// configuration map of the given layer, and that the final value equals the one given.
func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string, value any) {
	if assert == nil || v == nil ||
		len(keys) == 0 || keys[0] == "" {
		return
	}

	// init
	var val any
	var ms string
	switch l {
	case defaultLayer:
		val = v.defaults
		ms = "v.defaults"
	case overrideLayer:
		val = v.override
		ms = "v.override"
	}

	// loop through map
	var m map[string]any
	for _, k := range keys {
		if val == nil {
			assert.Failf("%s is not a map[string]any", ms)
			return
		}

		// deep scan of the map to get the final value
		switch val := val.(type) {
		case map[any]any:
			m = cast.ToStringMap(val)
		case map[string]any:
			m = val
		default:
			assert.Failf("%s is not a map[string]any", ms)
			return
		}
		ms = ms + "[\"" + k + "\"]"
		val = m[k]
	}
	assert.Equal(value, val)
}


================================================
FILE: remote/go.mod
================================================
module github.com/spf13/viper/remote

go 1.23.8

replace github.com/spf13/viper => ../

require (
	github.com/sagikazarmark/crypt v0.31.0
	github.com/spf13/viper v1.21.0
)

require (
	cloud.google.com/go v0.120.0 // indirect
	cloud.google.com/go/auth v0.16.5 // indirect
	cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
	cloud.google.com/go/compute/metadata v0.8.0 // indirect
	cloud.google.com/go/firestore v1.18.0 // indirect
	cloud.google.com/go/longrunning v0.6.7 // indirect
	github.com/armon/go-metrics v0.4.1 // indirect
	github.com/coreos/go-semver v0.3.1 // indirect
	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
	github.com/fatih/color v1.16.0 // indirect
	github.com/felixge/httpsnoop v1.0.4 // indirect
	github.com/fsnotify/fsnotify v1.9.0 // indirect
	github.com/go-logr/logr v1.4.3 // indirect
	github.com/go-logr/stdr v1.2.2 // indirect
	github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
	github.com/gogo/protobuf v1.3.2 // indirect
	github.com/golang/protobuf v1.5.4 // indirect
	github.com/google/s2a-go v0.1.9 // indirect
	github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
	github.com/googleapis/gax-go/v2 v2.15.0 // indirect
	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
	github.com/hashicorp/consul/api v1.32.1 // indirect
	github.com/hashicorp/errwrap v1.1.0 // indirect
	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
	github.com/hashicorp/go-hclog v1.5.0 // indirect
	github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
	github.com/hashicorp/go-multierror v1.1.1 // indirect
	github.com/hashicorp/go-rootcerts v1.0.2 // indirect
	github.com/hashicorp/golang-lru v0.5.4 // indirect
	github.com/hashicorp/serf v0.10.1 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/compress v1.18.0 // indirect
	github.com/mattn/go-colorable v0.1.13 // indirect
	github.com/mattn/go-isatty v0.0.20 // indirect
	github.com/mitchellh/go-homedir v1.1.0 // indirect
	github.com/mitchellh/mapstructure v1.5.0 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/nats-io/nats.go v1.45.0 // indirect
	github.com/nats-io/nkeys v0.4.11 // indirect
	github.com/nats-io/nuid v1.0.1 // indirect
	github.com/pelletier/go-toml/v2 v2.2.4 // indirect
	github.com/pkg/errors v0.9.1 // indirect
	github.com/sagikazarmark/locafero v0.11.0 // indirect
	github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
	github.com/spf13/afero v1.15.0 // indirect
	github.com/spf13/cast v1.10.0 // indirect
	github.com/spf13/pflag v1.0.10 // indirect
	github.com/subosito/gotenv v1.6.0 // indirect
	go.etcd.io/etcd/api/v3 v3.6.4 // indirect
	go.etcd.io/etcd/client/pkg/v3 v3.6.4 // indirect
	go.etcd.io/etcd/client/v2 v2.305.22 // indirect
	go.etcd.io/etcd/client/v3 v3.6.4 // indirect
	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
	go.opentelemetry.io/otel v1.37.0 // indirect
	go.opentelemetry.io/otel/metric v1.37.0 // indirect
	go.opentelemetry.io/otel/trace v1.37.0 // indirect
	go.uber.org/multierr v1.11.0 // indirect
	go.uber.org/zap v1.27.0 // indirect
	go.yaml.in/yaml/v3 v3.0.4 // indirect
	golang.org/x/crypto v0.41.0 // indirect
	golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
	golang.org/x/net v0.43.0 // indirect
	golang.org/x/oauth2 v0.30.0 // indirect
	golang.org/x/sync v0.16.0 // indirect
	golang.org/x/sys v0.35.0 // indirect
	golang.org/x/text v0.28.0 // indirect
	golang.org/x/time v0.12.0 // indirect
	google.golang.org/api v0.248.0 // indirect
	google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
	google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
	google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
	google.golang.org/grpc v1.75.0 // indirect
	google.golang.org/protobuf v1.36.7 // indirect
)


================================================
FILE: remote/go.sum
================================================
cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
cloud.google.com/go/firestore v1.18.0 h1:cuydCaLS7Vl2SatAeivXyhbhDEIR8BDmtn4egDhIn2s=
cloud.google.com/go/firestore v1.18.0/go.mod h1:5ye0v48PhseZBdcl0qbl3uttu7FIEwEYVaWm0UIEOEU=
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE=
github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4=
github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg=
github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/nats.go v1.45.0 h1:/wGPbnYXDM0pLKFjZTX+2JOw9TQPoIgTFrUaH97giwA=
github.com/nats-io/nats.go v1.45.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/crypt v0.31.0 h1:JJLrH7UojwA5KBkWuuk9x6UgHMzBaU2J2RHpEzUlpAc=
github.com/sagikazarmark/crypt v0.31.0/go.mod h1:X8SJJi7WiZU/Rgdr//EtoELirhl3vah7L7/fcBsO5Hk=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo=
go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk=
go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0=
go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI=
go.etcd.io/etcd/client/v2 v2.305.22 h1:FedDsGxor5iE9muhXm1CgE/TiSVOtgyB5+NYCHPzA2Q=
go.etcd.io/etcd/client/v2 v2.305.22/go.mod h1:VP7+1hEKyfGPuRdDmtT8GjM2HcVCKVlGmxfv3NwmrII=
go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A=
go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.248.0 h1:hUotakSkcwGdYUqzCRc5yGYsg4wXxpkKlW5ryVqvC1Y=
google.golang.org/api v0.248.0/go.mod h1:yAFUAF56Li7IuIQbTFoLwXTCI6XCFKueOlS7S9e4F9k=
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


================================================
FILE: remote/remote.go
================================================
// Copyright © 2015 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// Package remote integrates the remote features of Viper.
package remote

import (
	"bytes"
	"io"
	"os"
	"strings"

	crypt "github.com/sagikazarmark/crypt/config"

	"github.com/spf13/viper"
)

type remoteConfigProvider struct{}

func (rc remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) {
	cm, err := getConfigManager(rp)
	if err != nil {
		return nil, err
	}
	b, err := cm.Get(rp.Path())
	if err != nil {
		return nil, err
	}
	return bytes.NewReader(b), nil
}

func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) {
	cm, err := getConfigManager(rp)
	if err != nil {
		return nil, err
	}
	resp, err := cm.Get(rp.Path())
	if err != nil {
		return nil, err
	}

	return bytes.NewReader(resp), nil
}

func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {
	cm, err := getConfigManager(rp)
	if err != nil {
		return nil, nil
	}
	quit := make(chan bool)
	quitwc := make(chan bool)
	viperResponsCh := make(chan *viper.RemoteResponse)
	cryptoResponseCh := cm.Watch(rp.Path(), quit)
	// need this function to convert the Channel response form crypt.Response to viper.Response
	go func(cr <-chan *crypt.Response, vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) {
		for {
			select {
			case <-quitwc:
				quit <- true
				return
			case resp := <-cr:
				vr <- &viper.RemoteResponse{
					Error: resp.Error,
					Value: resp.Value,
				}
			}
		}
	}(cryptoResponseCh, viperResponsCh, quitwc, quit)

	return viperResponsCh, quitwc
}

func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
	var cm crypt.ConfigManager
	var err error

	endpoints := strings.Split(rp.Endpoint(), ";")
	if rp.SecretKeyring() != "" {
		var kr *os.File
		kr, err = os.Open(rp.SecretKeyring())
		if err != nil {
			return nil, err
		}
		defer kr.Close()
		switch rp.Provider() {
		case "etcd":
			cm, err = crypt.NewEtcdConfigManager(endpoints, kr)
		case "etcd3":
			cm, err = crypt.NewEtcdV3ConfigManager(endpoints, kr)
		case "firestore":
			cm, err = crypt.NewFirestoreConfigManager(endpoints, kr)
		case "nats":
			cm, err = crypt.NewNatsConfigManager(endpoints, kr)
		default:
			cm, err = crypt.NewConsulConfigManager(endpoints, kr)
		}
	} else {
		switch rp.Provider() {
		case "etcd":
			cm, err = crypt.NewStandardEtcdConfigManager(endpoints)
		case "etcd3":
			cm, err = crypt.NewStandardEtcdV3ConfigManager(endpoints)
		case "firestore":
			cm, err = crypt.NewStandardFirestoreConfigManager(endpoints)
		case "nats":
			cm, err = crypt.NewStandardNatsConfigManager(endpoints)
		default:
			cm, err = crypt.NewStandardConsulConfigManager(endpoints)
		}
	}
	if err != nil {
		return nil, err
	}
	return cm, nil
}

func init() {
	viper.RemoteConfig = &remoteConfigProvider{}
}


================================================
FILE: remote.go
================================================
package viper

import (
	"bytes"
	"fmt"
	"io"
	"reflect"
	"slices"
)

// SupportedRemoteProviders are universally supported remote providers.
var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"}

func resetRemote() {
	SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"}
}

type remoteConfigFactory interface {
	Get(rp RemoteProvider) (io.Reader, error)
	Watch(rp RemoteProvider) (io.Reader, error)
	WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool)
}

// RemoteResponse represents a response from a remote configuration provider.
type RemoteResponse struct {
	Value []byte
	Error error
}

// RemoteConfig is optional, see the remote package.
var RemoteConfig remoteConfigFactory

// UnsupportedRemoteProviderError denotes encountering an unsupported remote
// provider. Currently only etcd and Consul are supported.
type UnsupportedRemoteProviderError string

// Error returns the formatted remote provider error.
func (str UnsupportedRemoteProviderError) Error() string {
	return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str))
}

// RemoteConfigError denotes encountering an error while trying to
// pull the configuration from the remote provider.
type RemoteConfigError string

// Error returns the formatted remote provider error.
func (rce RemoteConfigError) Error() string {
	return fmt.Sprintf("Remote Configurations Error: %s", string(rce))
}

type defaultRemoteProvider struct {
	provider      string
	endpoint      string
	path          string
	secretKeyring string
}

func (rp defaultRemoteProvider) Provider() string {
	return rp.provider
}

func (rp defaultRemoteProvider) Endpoint() string {
	return rp.endpoint
}

func (rp defaultRemoteProvider) Path() string {
	return rp.path
}

func (rp defaultRemoteProvider) SecretKeyring() string {
	return rp.secretKeyring
}

// RemoteProvider stores the configuration necessary
// to connect to a remote key/value store.
// Optional secretKeyring to unencrypt encrypted values
// can be provided.
type RemoteProvider interface {
	Provider() string
	Endpoint() string
	Path() string
	SecretKeyring() string
}

// AddRemoteProvider adds a remote configuration source.
// Remote Providers are searched in the order they are added.
// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.
// endpoint is the url.  etcd requires http://ip:port, consul requires ip:port, nats requires nats://ip:port
// path is the path in the k/v store to retrieve configuration
// To retrieve a config file called myapp.json from /configs/myapp.json
// you should set path to /configs and set config name (SetConfigName()) to
// "myapp".
func AddRemoteProvider(provider, endpoint, path string) error {
	return v.AddRemoteProvider(provider, endpoint, path)
}

// AddRemoteProvider adds a remote configuration source.
// Remote Providers are searched in the order they are added.
// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.
// endpoint is the url.  etcd requires http://ip:port, consul requires ip:port, nats requires nats://ip:port
// path is the path in the k/v store to retrieve configuration
// To retrieve a config file called myapp.json from /configs/myapp.json
// you should set path to /configs and set config name (SetConfigName()) to
// "myapp".
func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
	if !slices.Contains(SupportedRemoteProviders, provider) {
		return UnsupportedRemoteProviderError(provider)
	}
	if provider != "" && endpoint != "" {
		v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint)

		rp := &defaultRemoteProvider{
			endpoint: endpoint,
			provider: provider,
			path:     path,
		}
		if !v.providerPathExists(rp) {
			v.remoteProviders = append(v.remoteProviders, rp)
		}
	}
	return nil
}

// AddSecureRemoteProvider adds a remote configuration source.
// Secure Remote Providers are searched in the order they are added.
// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.
// endpoint is the url.  etcd requires http://ip:port  consul requires ip:port
// secretkeyring is the filepath to your openpgp secret keyring.  e.g. /etc/secrets/myring.gpg
// path is the path in the k/v store to retrieve configuration
// To retrieve a config file called myapp.json from /configs/myapp.json
// you should set path to /configs and set config name (SetConfigName()) to
// "myapp".
// Secure Remote Providers are implemented with github.com/sagikazarmark/crypt.
func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
	return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring)
}

// AddSecureRemoteProvider adds a remote configuration source.
// Secure Remote Providers are searched in the order they are added.
// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.
// endpoint is the url.  etcd requires http://ip:port  consul requires ip:port
// secretkeyring is the filepath to your openpgp secret keyring.  e.g. /etc/secrets/myring.gpg
// path is the path in the k/v store to retrieve configuration
// To retrieve a config file called myapp.json from /configs/myapp.json
// you should set path to /configs and set config name (SetConfigName()) to
// "myapp".
// Secure Remote Providers are implemented with github.com/sagikazarmark/crypt.
func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
	if !slices.Contains(SupportedRemoteProviders, provider) {
		return UnsupportedRemoteProviderError(provider)
	}
	if provider != "" && endpoint != "" {
		v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint)

		rp := &defaultRemoteProvider{
			endpoint:      endpoint,
			provider:      provider,
			path:          path,
			secretKeyring: secretkeyring,
		}
		if !v.providerPathExists(rp) {
			v.remoteProviders = append(v.remoteProviders, rp)
		}
	}
	return nil
}

func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
	for _, y := range v.remoteProviders {
		if reflect.DeepEqual(y, p) {
			return true
		}
	}
	return false
}

// ReadRemoteConfig attempts to get configuration from a remote source
// and read it in the remote configuration registry.
func ReadRemoteConfig() error { return v.ReadRemoteConfig() }

// ReadRemoteConfig attempts to get configuration from a remote source
// and read it in the remote configuration registry.
func (v *Viper) ReadRemoteConfig() error {
	return v.getKeyValueConfig()
}

// WatchRemoteConfig updates configuration from available remote providers.
func WatchRemoteConfig() error { return v.WatchRemoteConfig() }

// WatchRemoteConfig updates configuration from available remote providers.
func (v *Viper) WatchRemoteConfig() error {
	return v.watchKeyValueConfig()
}

// WatchRemoteConfigOnChannel updates configuration from available remote providers.
func (v *Viper) WatchRemoteConfigOnChannel() error {
	return v.watchKeyValueConfigOnChannel()
}

// Retrieve the first found remote configuration.
func (v *Viper) getKeyValueConfig() error {
	if RemoteConfig == nil {
		return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'")
	}

	if len(v.remoteProviders) == 0 {
		return RemoteConfigError("No Remote Providers")
	}

	for _, rp := range v.remoteProviders {
		val, err := v.getRemoteConfig(rp)
		if err != nil {
			v.logger.Error(fmt.Errorf("get remote config: %w", err).Error())

			continue
		}

		v.kvstore = val

		return nil
	}
	return RemoteConfigError("No Files Found")
}

func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]any, error) {
	reader, err := RemoteConfig.Get(provider)
	if err != nil {
		return nil, err
	}
	err = v.unmarshalReader(reader, v.kvstore)
	return v.kvstore, err
}

// Retrieve the first found remote configuration.
func (v *Viper) watchKeyValueConfigOnChannel() error {
	if len(v.remoteProviders) == 0 {
		return RemoteConfigError("No Remote Providers")
	}

	for _, rp := range v.remoteProviders {
		respc, _ := RemoteConfig.WatchChannel(rp)
		// Todo: Add quit channel
		go func(rc <-chan *RemoteResponse) {
			for {
				b := <-rc
				reader := bytes.NewReader(b.Value)
				err := v.unmarshalReader(reader, v.kvstore)
				if err != nil {
					v.logger.Error(fmt.Errorf("failed to unmarshal remote config: %w", err).Error())
				}
			}
		}(respc)
		return nil
	}
	return RemoteConfigError("No Files Found")
}

// Retrieve the first found remote configuration.
func (v *Viper) watchKeyValueConfig() error {
	if len(v.remoteProviders) == 0 {
		return RemoteConfigError("No Remote Providers")
	}

	for _, rp := range v.remoteProviders {
		val, err := v.watchRemoteConfig(rp)
		if err != nil {
			v.logger.Error(fmt.Errorf("watch remote config: %w", err).Error())

			continue
		}
		v.kvstore = val
		return nil
	}
	return RemoteConfigError("No Files Found")
}

func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]any, error) {
	reader, err := RemoteConfig.Watch(provider)
	if err != nil {
		return nil, err
	}
	err = v.unmarshalReader(reader, v.kvstore)
	return v.kvstore, err
}


================================================
FILE: util.go
================================================
// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// Viper is a application configuration system.
// It believes that applications can be configured a variety of ways
// via flags, ENVIRONMENT variables, configuration files retrieved
// from the file system, or a remote key/value store.

package viper

import (
	"fmt"
	"log/slog"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"unicode"

	"github.com/spf13/cast"
)

// ConfigParseError denotes failing to parse configuration file.
type ConfigParseError struct {
	err error
}

// Error returns the formatted configuration error.
func (pe ConfigParseError) Error() string {
	return fmt.Sprintf("While parsing config: %s", pe.err.Error())
}

// Unwrap returns the wrapped error.
func (pe ConfigParseError) Unwrap() error {
	return pe.err
}

// toCaseInsensitiveValue checks if the value is a  map;
// if so, create a copy and lower-case the keys recursively.
func toCaseInsensitiveValue(value any) any {
	switch v := value.(type) {
	case map[any]any:
		value = copyAndInsensitiviseMap(cast.ToStringMap(v))
	case map[string]any:
		value = copyAndInsensitiviseMap(v)
	}

	return value
}

// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of
// any map it makes case insensitive.
func copyAndInsensitiviseMap(m map[string]any) map[string]any {
	nm := make(map[string]any)

	for key, val := range m {
		lkey := strings.ToLower(key)
		switch v := val.(type) {
		case map[any]any:
			nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
		case map[string]any:
			nm[lkey] = copyAndInsensitiviseMap(v)
		default:
			nm[lkey] = v
		}
	}

	return nm
}

func insensitiviseVal(val any) any {
	switch v := val.(type) {
	case map[any]any:
		// nested map: cast and recursively insensitivise
		val = cast.ToStringMap(val)
		insensitiviseMap(val.(map[string]any))
	case map[string]any:
		// nested map: recursively insensitivise
		insensitiviseMap(v)
	case []any:
		// nested array: recursively insensitivise
		insensitiveArray(v)
	}
	return val
}

func insensitiviseMap(m map[string]any) {
	for key, val := range m {
		val = insensitiviseVal(val)
		lower := strings.ToLower(key)
		if key != lower {
			// remove old key (not lower-cased)
			delete(m, key)
		}
		// update map
		m[lower] = val
	}
}

func insensitiveArray(a []any) {
	for i, val := range a {
		a[i] = insensitiviseVal(val)
	}
}

func absPathify(logger *slog.Logger, inPath string) string {
	logger.Info("trying to resolve absolute path", "path", inPath)

	if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
		inPath = userHomeDir() + inPath[5:]
	}

	inPath = os.ExpandEnv(inPath)

	if filepath.IsAbs(inPath) {
		return filepath.Clean(inPath)
	}

	p, err := filepath.Abs(inPath)
	if err == nil {
		return filepath.Clean(p)
	}

	logger.Error(fmt.Errorf("could not discover absolute path: %w", err).Error())

	return ""
}

func userHomeDir() string {
	if runtime.GOOS == "windows" {
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if home == "" {
			home = os.Getenv("USERPROFILE")
		}
		return home
	}
	return os.Getenv("HOME")
}

func safeMul(a, b uint) uint {
	c := a * b
	if a > 1 && b > 1 && c/b != a {
		return 0
	}
	return c
}

// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes.
func parseSizeInBytes(sizeStr string) uint {
	sizeStr = strings.TrimSpace(sizeStr)
	lastChar := len(sizeStr) - 1
	multiplier := uint(1)

	if lastChar > 0 {
		if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {
			if lastChar > 1 {
				switch unicode.ToLower(rune(sizeStr[lastChar-1])) {
				case 'k':
					multiplier = 1 << 10
					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
				case 'm':
					multiplier = 1 << 20
					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
				case 'g':
					multiplier = 1 << 30
					sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
				default:
					multiplier = 1
					sizeStr = strings.TrimSpace(sizeStr[:lastChar])
				}
			}
		}
	}

	size := max(cast.ToInt(sizeStr), 0)

	return safeMul(uint(size), multiplier)
}

// deepSearch scans deep maps, following the key indexes listed in the
// sequence "path".
// The last value is expected to be another map, and is returned.
//
// In case intermediate keys do not exist, or map to a non-map value,
// a new map is created and inserted, and the search continues from there:
// the initial map "m" may be modified!
func deepSearch(m map[string]any, path []string) map[string]any {
	for _, k := range path {
		m2, ok := m[k]
		if !ok {
			// intermediate key does not exist
			// => create it and continue from there
			m3 := make(map[string]any)
			m[k] = m3
			m = m3
			continue
		}
		m3, ok := m2.(map[string]any)
		if !ok {
			// intermediate key is a value
			// => replace with a new map
			m3 = make(map[string]any)
			m[k] = m3
		}
		// continue search from here
		m = m3
	}
	return m
}


================================================
FILE: util_test.go
================================================
// Copyright © 2016 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// Viper is a application configuration system.
// It believes that applications can be configured a variety of ways
// via flags, ENVIRONMENT variables, configuration files retrieved
// from the file system, or a remote key/value store.

package viper

import (
	"log/slog"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestCopyAndInsensitiviseMap(t *testing.T) {
	var (
		given = map[string]any{
			"Foo": 32,
			"Bar": map[any]any{
				"ABc": "A",
				"cDE": "B",
			},
		}
		expected = map[string]any{
			"foo": 32,
			"bar": map[string]any{
				"abc": "A",
				"cde": "B",
			},
		}
	)

	got := copyAndInsensitiviseMap(given)

	assert.Equal(t, expected, got)
	_, ok := given["foo"]
	assert.False(t, ok)
	_, ok = given["bar"]
	assert.False(t, ok)

	m := given["Bar"].(map[any]any)
	_, ok = m["ABc"]
	assert.True(t, ok)
}

func TestAbsPathify(t *testing.T) {
	skipWindows(t)

	home := userHomeDir()
	homer := filepath.Join(home, "homer")
	wd, _ := os.Getwd()

	t.Setenv("HOMER_ABSOLUTE_PATH", homer)
	t.Setenv("VAR_WITH_RELATIVE_PATH", "relative")

	tests := []struct {
		input  string
		output string
	}{
		{"", wd},
		{"sub", filepath.Join(wd, "sub")},
		{"./", wd},
		{"./sub", filepath.Join(wd, "sub")},
		{"$HOME", home},
		{"$HOME/", home},
		{"$HOME/sub", filepath.Join(home, "sub")},
		{"$HOMER_ABSOLUTE_PATH", homer},
		{"$HOMER_ABSOLUTE_PATH/", homer},
		{"$HOMER_ABSOLUTE_PATH/sub", filepath.Join(homer, "sub")},
		{"$VAR_WITH_RELATIVE_PATH", filepath.Join(wd, "relative")},
		{"$VAR_WITH_RELATIVE_PATH/", filepath.Join(wd, "relative")},
		{"$VAR_WITH_RELATIVE_PATH/sub", filepath.Join(wd, "relative", "sub")},
	}

	for _, test := range tests {
		got := absPathify(slog.Default(), test.input)
		assert.Equal(t, test.output, got)
	}
}


================================================
FILE: viper.go
================================================
// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// Viper is an application configuration system.
// It believes that applications can be configured a variety of ways
// via flags, ENVIRONMENT variables, configuration files retrieved
// from the file system, or a remote key/value store.

// Each item takes precedence over the item below it:

// overrides
// flag
// env
// config
// key/value store
// default

package viper

import (
	"bytes"
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	fs "io/fs"
	"log/slog"
	"os"
	"path/filepath"
	"reflect"
	"slices"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/go-viper/mapstructure/v2"
	"github.com/spf13/afero"
	"github.com/spf13/cast"
	"github.com/spf13/pflag"

	"github.com/spf13/viper/internal/features"
)

var v *Viper

func init() {
	v = New()
}

// A DecoderConfigOption can be passed to viper.Unmarshal to configure
// mapstructure.DecoderConfig options.
type DecoderConfigOption func(*mapstructure.DecoderConfig)

// DecodeHook returns a DecoderConfigOption which overrides the default
// DecoderConfig.DecodeHook value, the default is:
//
//	 mapstructure.ComposeDecodeHookFunc(
//			mapstructure.StringToTimeDurationHookFunc(),
//			mapstructure.StringToSliceHookFunc(","),
//		)
func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
	return func(c *mapstructure.DecoderConfig) {
		c.DecodeHook = hook
	}
}

// Viper is a prioritized configuration registry. It
// maintains a set of configuration sources, fetches
// values to populate those, and provides them according
// to the source's priority.
// The priority of the sources is the following:
// 1. overrides
// 2. flags
// 3. env. variables
// 4. config file
// 5. key/value store
// 6. defaults
//
// For example, if values from the following sources were loaded:
//
//	Defaults : {
//		"secret": "",
//		"user": "default",
//		"endpoint": "https://localhost"
//	}
//	Config : {
//		"user": "root"
//		"secret": "defaultsecret"
//	}
//	Env : {
//		"secret": "somesecretkey"
//	}
//
// The resulting config will have the following values:
//
//	{
//		"secret": "somesecretkey",
//		"user": "root",
//		"endpoint": "https://localhost"
//	}
//
// Note: Vipers are not safe for concurrent Get() and Set() operations.
type Viper struct {
	// Delimiter that separates a list of keys
	// used to access a nested value in one go
	keyDelim string

	// A set of paths to look for the config file in
	configPaths []string

	// The filesystem to read config from.
	fs afero.Fs

	finder Finder

	// A set of remote providers to search for the configuration
	remoteProviders []*defaultRemoteProvider

	// Name of file to look for inside the path
	configName        string
	configFile        string
	configType        string
	configPermissions os.FileMode
	envPrefix         string

	automaticEnvApplied bool
	envKeyReplacer      StringReplacer
	allowEmptyEnv       bool

	parents        []string
	config         map[string]any
	override       map[string]any
	defaults       map[string]any
	kvstore        map[string]any
	pflags         map[string]FlagValue
	env            map[string][]string
	aliases        map[string]string
	typeByDefValue bool

	onConfigChange func(fsnotify.Event)

	logger *slog.Logger

	encoderRegistry EncoderRegistry
	decoderRegistry DecoderRegistry

	decodeHook mapstructure.DecodeHookFunc

	experimentalFinder     bool
	experimentalBindStruct bool
}

// New returns an initialized Viper instance.
func New() *Viper {
	v := new(Viper)
	v.keyDelim = "."
	v.configName = "config"
	v.configPermissions = os.FileMode(0o644)
	v.fs = afero.NewOsFs()
	v.config = make(map[string]any)
	v.parents = []string{}
	v.override = make(map[string]any)
	v.defaults = make(map[string]any)
	v.kvstore = make(map[string]any)
	v.pflags = make(map[string]FlagValue)
	v.env = make(map[string][]string)
	v.aliases = make(map[string]string)
	v.typeByDefValue = false
	v.logger = slog.New(&discardHandler{})

	codecRegistry := NewCodecRegistry()

	v.encoderRegistry = codecRegistry
	v.decoderRegistry = codecRegistry

	v.experimentalFinder = features.Finder
	v.experimentalBindStruct = features.BindStruct

	return v
}

// Option configures Viper using the functional options paradigm popularized by Rob Pike and Dave Cheney.
// If you're unfamiliar with this style,
// see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and
// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis.
type Option interface {
	apply(v *Viper)
}

type optionFunc func(v *Viper)

func (fn optionFunc) apply(v *Viper) {
	fn(v)
}

// KeyDelimiter sets the delimiter used for determining key parts.
// By default it's value is ".".
func KeyDelimiter(d string) Option {
	return optionFunc(func(v *Viper) {
		v.keyDelim = d
	})
}

// StringReplacer applies a set of replacements to a string.
type StringReplacer interface {
	// Replace returns a copy of s with all replacements performed.
	Replace(s string) string
}

// EnvKeyReplacer sets a replacer used for mapping environment variables to internal keys.
func EnvKeyReplacer(r StringReplacer) Option {
	return optionFunc(func(v *Viper) {
		if r == nil {
			return
		}

		v.envKeyReplacer = r
	})
}

// WithDecodeHook sets a default decode hook for mapstructure.
func WithDecodeHook(h mapstructure.DecodeHookFunc) Option {
	return optionFunc(func(v *Viper) {
		if h == nil {
			return
		}

		v.decodeHook = h
	})
}

// NewWithOptions creates a new Viper instance.
func NewWithOptions(opts ...Option) *Viper {
	v := New()

	for _, opt := range opts {
		opt.apply(v)
	}

	return v
}

// SetOptions sets the options on the global Viper instance.
//
// Be careful when using this function: subsequent calls may override options you set.
// It's always better to use a local Viper instance.
func SetOptions(opts ...Option) {
	for _, opt := range opts {
		opt.apply(v)
	}
}

// Reset is intended for testing, will reset all to default settings.
// In the public interface for the viper package so applications
// can use it in their testing as well.
func Reset() {
	v = New()
	SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}

	resetRemote()
}

// SupportedExts are universally supported extensions.
var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}

// OnConfigChange sets the event handler that is called when a config file changes.
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }

// OnConfigChange sets the event handler that is called when a config file changes.
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
	v.onConfigChange = run
}

// WatchConfig starts watching a config file for changes.
func WatchConfig() { v.WatchConfig() }

// WatchConfig starts watching a config file for changes.
func (v *Viper) WatchConfig() {
	initWG := sync.WaitGroup{}
	initWG.Add(1)
	go func() {
		watcher, err := fsnotify.NewWatcher()
		if err != nil {
			v.logger.Error(fmt.Sprintf("failed to create watcher: %s", err))
			os.Exit(1)
		}
		defer watcher.Close()
		// we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
		filename, err := v.getConfigFile()
		if err != nil {
			v.logger.Error(fmt.Sprintf("get config file: %s", err))
			initWG.Done()
			return
		}

		configFile := filepath.Clean(filename)
		configDir, _ := filepath.Split(configFile)
		realConfigFile, _ := filepath.EvalSymlinks(filename)

		eventsWG := sync.WaitGroup{}
		eventsWG.Add(1)
		go func() {
			for {
				select {
				case event, ok := <-watcher.Events:
					if !ok { // 'Events' channel is closed
						eventsWG.Done()
						return
					}
					currentConfigFile, _ := filepath.EvalSymlinks(filename)
					// we only care about the config file with the following cases:
					// 1 - if the config file was modified or created
					// 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement)
					if (filepath.Clean(event.Name) == configFile &&
						(event.Has(fsnotify.Write) || event.Has(fsnotify.Create))) ||
						(currentConfigFile != "" && currentConfigFile != realConfigFile) {
						realConfigFile = currentConfigFile
						err := v.ReadInConfig()
						if err != nil {
							v.logger.Error(fmt.Sprintf("read config file: %s", err))
						}
						if v.onConfigChange != nil {
							v.onConfigChange(event)
						}
					} else if filepath.Clean(event.Name) == configFile && event.Has(fsnotify.Remove) {
						eventsWG.Done()
						return
					}

				case err, ok := <-watcher.Errors:
					if ok { // 'Errors' channel is not closed
						v.logger.Error(fmt.Sprintf("watcher error: %s", err))
					}
					eventsWG.Done()
					return
				}
			}
		}()
		err = watcher.Add(configDir)
		if err != nil {
			v.logger.Error(fmt.Sprintf("failed to add watcher: %s", err))
			initWG.Done()
			return
		}
		initWG.Done()   // done initializing the watch in this go routine, so the parent routine can move on...
		eventsWG.Wait() // now, wait for event loop to end in this go-routine...
	}()
	initWG.Wait() // make sure that the go routine above fully ended before returning
}

// SetConfigFile explicitly defines the path, name and extension of the config file.
// Viper will use this and not check any of the config paths.
func SetConfigFile(in string) { v.SetConfigFile(in) }

// SetConfigFile explicitly defines the path, name and extension of the config file.
// Viper will use this and not check any of the config paths.
func (v *Viper) SetConfigFile(in string) {
	if in != "" {
		v.configFile = in
	}
}

// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.
// E.g. if your prefix is "spf", the env registry will look for env
// variables that start with "SPF_".
func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }

// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.
// E.g. if your prefix is "spf", the env registry will look for env
// variables that start with "SPF_".
func (v *Viper) SetEnvPrefix(in string) {
	if in != "" {
		v.envPrefix = in
	}
}

// GetEnvPrefix returns the environment variable prefix.
func GetEnvPrefix() string { return v.GetEnvPrefix() }

// GetEnvPrefix returns the environment variable prefix.
func (v *Viper) GetEnvPrefix() string {
	return v.envPrefix
}

func (v *Viper) mergeWithEnvPrefix(in string) string {
	if v.envPrefix != "" {
		return strings.ToUpper(v.envPrefix + "_" + in)
	}

	return strings.ToUpper(in)
}

// AllowEmptyEnv tells Viper to consider set,
// but empty environment variables as valid values instead of falling back.
// For backward compatibility reasons this is false by default.
func AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) }

// AllowEmptyEnv tells Viper to consider set,
// but empty environment variables as valid values instead of falling back.
// For backward compatibility reasons this is false by default.
func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) {
	v.allowEmptyEnv = allowEmptyEnv
}

// TODO: should getEnv logic be moved into find(). Can generalize the use of
// rewriting keys many things, Ex: Get('someKey') -> some_key
// (camel case to snake case for JSON keys perhaps)

// getEnv is a wrapper around os.Getenv which replaces characters in the original
// key. This allows env vars which have different keys than the config object
// keys.
func (v *Viper) getEnv(key string) (string, bool) {
	if v.envKeyReplacer != nil {
		key = v.envKeyReplacer.Replace(key)
	}

	val, ok := os.LookupEnv(key)

	return val, ok && (v.allowEmptyEnv || val != "")
}

// ConfigFileUsed returns the file used to populate the config registry.
func ConfigFileUsed() string { return v.ConfigFileUsed() }

// ConfigFileUsed returns the file used to populate the config registry.
func (v *Viper) ConfigFileUsed() string { return v.configFile }

// AddConfigPath adds a path for Viper to search for the config file in.
// Can be called multiple times to define multiple search paths.
func AddConfigPath(in string) { v.AddConfigPath(in) }

// AddConfigPath adds a path for Viper to search for the config file in.
// Can be called multiple times to define multiple search paths.
func (v *Viper) AddConfigPath(in string) {
	if v.finder != nil {
		v.logger.Warn("ineffective call to function: custom finder takes precedence", slog.String("function", "AddConfigPath"))
	}

	if in != "" {
		absin := absPathify(v.logger, in)

		v.logger.Info("adding path to search paths", "path", absin)
		if !slices.Contains(v.configPaths, absin) {
			v.configPaths = append(v.configPaths, absin)
		}
	}
}

// searchMap recursively searches for a value for path in source map.
// Returns nil if not found.
// Note: This assumes that the path entries and map keys are lower cased.
func (v *Viper) searchMap(source map[string]any, path []string) any {
	if len(path) == 0 {
		return source
	}

	next, ok := source[path[0]]
	if ok {
		// Fast path
		if len(path) == 1 {
			return next
		}

		// Nested case
		switch next := next.(type) {
		case map[any]any:
			return v.searchMap(cast.ToStringMap(next), path[1:])
		case map[string]any:
			// Type assertion is safe here since it is only reached
			// if the type of `next` is the same as the type being asserted
			return v.searchMap(next, path[1:])
		default:
			// got a value but nested key expected, return "nil" for not found
			return nil
		}
	}
	return nil
}

// searchIndexableWithPathPrefixes recursively searches for a value for path in source map/slice.
//
// While searchMap() considers each path element as a single map key or slice index, this
// function searches for, and prioritizes, merged path elements.
// e.g., if in the source, "foo" is defined with a sub-key "bar", and "foo.bar"
// is also defined, this latter value is returned for path ["foo", "bar"].
//
// This should be useful only at config level (other maps may not contain dots
// in their keys).
//
// Note: This assumes that the path entries and map keys are lower cased.
func (v *Viper) searchIndexableWithPathPrefixes(source any, path []string) any {
	if len(path) == 0 {
		return source
	}

	// search for path prefixes, starting from the longest one
	for i := len(path); i > 0; i-- {
		prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))

		var val any
		switch sourceIndexable := source.(type) {
		case []any:
			val = v.searchSliceWithPathPrefixes(sourceIndexable, prefixKey, i, path)
		case map[string]any:
			val = v.searchMapWithPathPrefixes(sourceIndexable, prefixKey, i, path)
		}
		if val != nil {
			return val
		}
	}

	// not found
	return nil
}

// searchSliceWithPathPrefixes searches for a value for path in sourceSlice
//
// This function is part of the searchIndexableWithPathPrefixes recurring search and
// should not be called directly from functions other than searchIndexableWithPathPrefixes.
func (v *Viper) searchSliceWithPathPrefixes(
	sourceSlice []any,
	prefixKey string,
	pathIndex int,
	path []string,
) any {
	// if the prefixKey is not a number or it is out of bounds of the slice
	index, err := strconv.Atoi(prefixKey)
	if err != nil || len(sourceSlice) <= index {
		return nil
	}

	next := sourceSlice[index]

	// Fast path
	if pathIndex == len(path) {
		return next
	}

	switch n := next.(type) {
	case map[any]any:
		return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])
	case map[string]any, []any:
		return v.searchIndexableWithPathPrefixes(n, path[pathIndex:])
	default:
		// got a value but nested key expected, do nothing and look for next prefix
	}

	// not found
	return nil
}

// searchMapWithPathPrefixes searches for a value for path in sourceMap
//
// This function is part of the searchIndexableWithPathPrefixes recurring search and
// should not be called directly from functions other than searchIndexableWithPathPrefixes.
func (v *Viper) searchMapWithPathPrefixes(
	sourceMap map[string]any,
	prefixKey string,
	pathIndex int,
	path []string,
) any {
	next, ok := sourceMap[prefixKey]
	if !ok {
		return nil
	}

	// Fast path
	if pathIndex == len(path) {
		return next
	}

	// Nested case
	switch n := next.(type) {
	case map[any]any:
		return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])
	case map[string]any, []any:
		return v.searchIndexableWithPathPrefixes(n, path[pathIndex:])
	default:
		// got a value but nested key expected, do nothing and look for next prefix
	}

	// not found
	return nil
}

// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere
// on its path in the map.
// e.g., if "foo.bar" has a value in the given map, it “shadows”
//
//	"foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]any) string {
	var parentVal any
	for i := 1; i < len(path); i++ {
		parentVal = v.searchMap(m, path[0:i])
		if parentVal == nil {
			// not found, no need to add more path elements
			return ""
		}
		switch parentVal.(type) {
		case map[any]any:
			continue
		case map[string]any:
			continue
		default:
			// parentVal is a regular value which shadows "path"
			return strings.Join(path[0:i], v.keyDelim)
		}
	}
	return ""
}

// isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere
// in a sub-path of the map.
// e.g., if "foo.bar" has a value in the given map, it “shadows”
//
//	"foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInFlatMap(path []string, mi any) string {
	// unify input map
	var m map[string]interface{}
	switch miv := mi.(type) {
	case map[string]string:
		m = castMapStringToMapInterface(miv)
	case map[string]FlagValue:
		m = castMapFlagToMapInterface(miv)
	default:
		return ""
	}

	// scan paths
	var parentKey string
	for i := 1; i < len(path); i++ {
		parentKey = strings.Join(path[0:i], v.keyDelim)
		if _, ok := m[parentKey]; ok {
			return parentKey
		}
	}
	return ""
}

// isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere
// in the environment, when automatic env is on.
// e.g., if "foo.bar" has a value in the environment, it “shadows”
//
//	"foo.bar.baz" in a lower-priority map
func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
	var parentKey string
	for i := 1; i < len(path); i++ {
		parentKey = strings.Join(path[0:i], v.keyDelim)
		if _, ok := v.getEnv(v.mergeWithEnvPrefix(parentKey)); ok {
			return parentKey
		}
	}
	return ""
}

// SetTypeByDefaultValue enables or disables the inference of a key value's
// type when the Get function is used based upon a key's default value as
// opposed to the value returned based on the normal fetch logic.
//
// For example, if a key has a default value of []string{} and the same key
// is set via an environment variable to "a b c", a call to the Get function
// would return a string slice for the key if the key's type is inferred by
// the default value and the Get function would return:
//
//	[]string {"a", "b", "c"}
//
// Otherwise the Get function would return:
//
//	"a b c"
func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }

// SetTypeByDefaultValue enables or disables the inference of a key value's
// type when the Get function is used based upon a key's default value as
// opposed to the value returned based on the normal fetch logic.
//
// For example, if a key has a default value of []string{} and the same key
// is set via an environment variable to "a b c", a call to the Get function
// would return a string slice for the key if the key's type is inferred by
// the default value and the Get function would return:
//
//	[]string {"a", "b", "c"}
//
// Otherwise the Get function would return:
//
//	"a b c"
func (v *Viper) SetTypeByDefaultValue(enable bool) {
	v.typeByDefValue = enable
}

// GetViper gets the global Viper instance.
func GetViper() *Viper {
	return v
}

// Get can retrieve any value given the key to use.
// Get is case-insensitive for a key.
// Get has the behavior of returning the value associated with the first
// place from where it is set. Viper will check in the following order:
// override, flag, env, config file, key/value store, default
//
// Get returns an interface. For a specific value use one of the Get____ methods.
func Get(key string) any { return v.Get(key) }

// Get retrieves the value associated with the key.
// Get is case-insensitive for a key.
// Get has the behavior of returning the value associated with the first
// place from where it is set. Viper will check in the following order:
// override, flag, env, config file, key/value store, default
//
// Get returns an interface. For a specific value use one of the Get____ methods.
func (v *Viper) Get(key string) any {
	lcaseKey := strings.ToLower(key)
	val := v.find(lcaseKey, true)
	if val == nil {
		return nil
	}

	if v.typeByDefValue {
		// TODO(bep) this branch isn't covered by a single test.
		valType := val
		path := strings.Split(lcaseKey, v.keyDelim)
		defVal := v.searchMap(v.defaults, path)
		if defVal != nil {
			valType = defVal
		}

		switch valType.(type) {
		case bool:
			return cast.ToBool(val)
		case string:
			return cast.ToString(val)
		case int32, int16, int8, int:
			return cast.ToInt(val)
		case uint:
			return cast.ToUint(val)
		case uint32:
			return cast.ToUint32(val)
		case uint64:
			return cast.ToUint64(val)
		case int64:
			return cast.ToInt64(val)
		case float64, float32:
			return cast.ToFloat64(val)
		case time.Time:
			return cast.ToTime(val)
		case time.Duration:
			return cast.ToDuration(val)
		case []string:
			return cast.ToStringSlice(val)
		case []int:
			return cast.ToIntSlice(val)
		case []time.Duration:
			return cast.ToDurationSlice(val)
		}
	}

	return val
}

// Sub returns new Viper instance representing a sub tree of this instance.
// Sub is case-insensitive for a key.
func Sub(key string) *Viper { return v.Sub(key) }

// Sub returns a new Viper instance representing a sub tree of this instance.
// Sub is case-insensitive for a key.
func (v *Viper) Sub(key string) *Viper {
	subv := New()
	data := v.Get(key)
	if data == nil {
		return nil
	}

	if reflect.TypeOf(data).Kind() == reflect.Map {
		subv.parents = append([]string(nil), v.parents...)
		subv.parents = append(subv.parents, strings.ToLower(key))
		subv.automaticEnvApplied = v.automaticEnvApplied
		subv.envPrefix = v.envPrefix
		subv.envKeyReplacer = v.envKeyReplacer
		subv.keyDelim = v.keyDelim
		subv.config = cast.ToStringMap(data)
		return subv
	}
	return nil
}

// GetString returns the value associated with the key as a string.
func GetString(key string) string { return v.GetString(key) }

// GetString returns the value associated with the key as a string.
func (v *Viper) GetString(key string) string {
	return cast.ToString(v.Get(key))
}

// GetBool returns the value associated with the key as a boolean.
func GetBool(key string) bool { return v.GetBool(key) }

// GetBool returns the value associated with the key as a boolean.
func (v *Viper) GetBool(key string) bool {
	return cast.ToBool(v.Get(key))
}

// GetInt returns the value associated with the key as an integer.
func GetInt(key string) int { return v.GetInt(key) }

// GetInt returns the value associated with the key as an integer.
func (v *Viper) GetInt(key string) int {
	return cast.ToInt(v.Get(key))
}

// GetInt32 returns the value associated with the key as an integer.
func GetInt32(key string) int32 { return v.GetInt32(key) }

// GetInt32 returns the value associated with the key as an integer.
func (v *Viper) GetInt32(key string) int32 {
	return cast.ToInt32(v.Get(key))
}

// GetInt64 returns the value associated with the key as an integer.
func GetInt64(key string) int64 { return v.GetInt64(key) }

// GetInt64 returns the value associated with the key as an integer.
func (v *Viper) GetInt64(key string) int64 {
	return cast.ToInt64(v.Get(key))
}

// GetUint8 returns the value associated with the key as an unsigned integer.
func GetUint8(key string) uint8 { return v.GetUint8(key) }

// GetUint8 returns the value associated with the key as an unsigned integer.
func (v *Viper) GetUint8(key string) uint8 {
	return cast.ToUint8(v.Get(key))
}

// GetUint returns the value associated with the key as an unsigned integer.
func GetUint(key string) uint { return v.GetUint(key) }

// GetUint returns the value associated with the key as an unsigned integer.
func (v *Viper) GetUint(key string) uint {
	return cast.ToUint(v.Get(key))
}

// GetUint16 returns the value associated with the key as an unsigned integer.
func GetUint16(key string) uint16 { return v.GetUint16(key) }

// GetUint16 returns the value associated with the key as an unsigned integer.
func (v *Viper) GetUint16(key string) uint16 {
	return cast.ToUint16(v.Get(key))
}

// GetUint32 returns the value associated with the key as an unsigned integer.
func GetUint32(key string) uint32 { return v.GetUint32(key) }

// GetUint32 returns the value associated with the key as an unsigned integer.
func (v *Viper) GetUint32(key string) uint32 {
	return cast.ToUint32(v.Get(key))
}

// GetUint64 returns the value associated with the key as an unsigned integer.
func GetUint64(key string) uint64 { return v.GetUint64(key) }

// GetUint64 returns the value associated with the key as an unsigned integer.
func (v *Viper) GetUint64(key string) uint64 {
	return cast.ToUint64(v.Get(key))
}

// GetFloat64 returns the value associated with the key as a float64.
func GetFloat64(key string) float64 { return v.GetFloat64(key) }

// GetFloat64 returns the value associated with the key as a float64.
func (v *Viper) GetFloat64(key string) float64 {
	return cast.ToFloat64(v.Get(key))
}

// GetTime returns the value associated with the key as time.
func GetTime(key string) time.Time { return v.GetTime(key) }

// GetTime returns the value associated with the key as time.
func (v *Viper) GetTime(key string) time.Time {
	return cast.ToTime(v.Get(key))
}

// GetDuration returns the value associated with the key as a duration.
func GetDuration(key string) time.Duration { return v.GetDuration(key) }

// GetDuration returns the value associated with the key as a duration.
func (v *Viper) GetDuration(key string) time.Duration {
	return cast.ToDuration(v.Get(key))
}

// GetIntSlice returns the value associated with the key as a slice of int values.
func GetIntSlice(key string) []int { return v.GetIntSlice(key) }

// GetIntSlice returns the value associated with the key as a slice of int values.
func (v *Viper) GetIntSlice(key string) []int {
	return cast.ToIntSlice(v.Get(key))
}

// GetStringSlice returns the value associated with the key as a slice of strings.
func GetStringSlice(key string) []string { return v.GetStringSlice(key) }

// GetStringSlice returns the value associated with the key as a slice of strings.
func (v *Viper) GetStringSlice(key string) []string {
	return cast.ToStringSlice(v.Get(key))
}

// GetStringMap returns the value associated with the key as a map of interfaces.
func GetStringMap(key string) map[string]any { return v.GetStringMap(key) }

// GetStringMap returns the value associated with the key as a map of interfaces.
func (v *Viper) GetStringMap(key string) map[string]any {
	return cast.ToStringMap(v.Get(key))
}

// GetStringMapString returns the value associated with the key as a map of strings.
func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) }

// GetStringMapString returns the value associated with the key as a map of strings.
func (v *Viper) GetStringMapString(key string) map[string]string {
	return cast.ToStringMapString(v.Get(key))
}

// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) }

// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {
	return cast.ToStringMapStringSlice(v.Get(key))
}

// GetSizeInBytes returns the size of the value associated with the given key
// in bytes.
func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) }

// GetSizeInBytes returns the size of the value associated with the given key
// in bytes.
func (v *Viper) GetSizeInBytes(key string) uint {
	sizeStr := cast.ToString(v.Get(key))
	return parseSizeInBytes(sizeStr)
}

// UnmarshalKey takes a single key and unmarshals it into a Struct.
func UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error {
	return v.UnmarshalKey(key, rawVal, opts...)
}

// UnmarshalKey takes a single key and unmarshals it into a Struct.
func (v *Viper) UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error {
	return decode(v.Get(key), v.defaultDecoderConfig(rawVal, opts...))
}

// Unmarshal unmarshals the config into a Struct. Make sure that the tags
// on the fields of the structure are properly set.
func Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
	return v.Unmarshal(rawVal, opts...)
}

// Unmarshal unmarshals the config into a Struct. Make sure that the tags
// on the fields of the structure are properly set.
func (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
	keys := v.AllKeys()

	if v.experimentalBindStruct {
		// TODO: make this optional?
		structKeys, err := v.decodeStructKeys(rawVal, opts...)
		if err != nil {
			return err
		}

		keys = append(keys, structKeys...)
	}

	// TODO: struct keys should be enough?
	return decode(v.getSettings(keys), v.defaultDecoderConfig(rawVal, opts...))
}

func (v *Viper) decodeStructKeys(input any, opts ...DecoderConfigOption) ([]string, error) {
	var structKeyMap map[string]any

	err := decode(input, v.defaultDecoderConfig(&structKeyMap, opts...))
	if err != nil {
		return nil, err
	}

	flattenedStructKeyMap := v.flattenAndMergeMap(map[string]bool{}, structKeyMap, "")

	r := make([]string, 0, len(flattenedStructKeyMap))
	for v := range flattenedStructKeyMap {
		r = append(r, v)
	}

	return r, nil
}

// defaultDecoderConfig returns default mapstructure.DecoderConfig with support
// of time.Duration values & string slices.
func (v *Viper) defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {
	decodeHook := v.decodeHook
	if decodeHook == nil {
		decodeHook = mapstructure.ComposeDecodeHookFunc(
			mapstructure.StringToTimeDurationHookFunc(),
			// mapstructure.StringToSliceHookFunc(","),
			stringToWeakSliceHookFunc(","),
		)
	}

	c := &mapstructure.DecoderConfig{
		Metadata:         nil,
		WeaklyTypedInput: true,
		DecodeHook:       decodeHook,
	}

	for _, opt := range opts {
		opt(c)
	}

	// Do not allow overwriting the output
	c.Result = output

	return c
}

// As of mapstructure v2.0.0 StringToSliceHookFunc checks if the return type is a string slice.
// This function removes that check.
// TODO: implement a function that checks if the value can be converted to the return type and use it instead.
func stringToWeakSliceHookFunc(sep string) mapstructure.DecodeHookFunc {
	return func(
		f reflect.Type,
		t reflect.Type,
		data interface{},
	) (interface{}, error) {
		if f.Kind() != reflect.String || t.Kind() != reflect.Slice {
			return data, nil
		}

		raw := data.(string)
		if raw == "" {
			return []string{}, nil
		}

		return strings.Split(raw, sep), nil
	}
}

// decode is a wrapper around mapstructure.Decode that mimics the WeakDecode functionality.
func decode(input any, config *mapstructure.DecoderConfig) error {
	decoder, err := mapstructure.NewDecoder(config)
	if err != nil {
		return err
	}
	return decoder.Decode(input)
}

// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
// in the destination struct.
func UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {
	return v.UnmarshalExact(rawVal, opts...)
}

// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
// in the destination struct.
func (v *Viper) UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {
	config := v.defaultDecoderConfig(rawVal, opts...)
	config.ErrorUnused = true

	keys := v.AllKeys()

	if v.experimentalBindStruct {
		// TODO: make this optional?
		structKeys, err := v.decodeStructKeys(rawVal, opts...)
		if err != nil {
			return err
		}

		keys = append(keys, structKeys...)
	}

	// TODO: struct keys should be enough?
	return decode(v.getSettings(keys), config)
}

// BindPFlags binds a full flag set to the configuration, using each flag's long
// name as the config key.
func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) }

// BindPFlags binds a full flag set to the configuration, using each flag's long
// name as the config key.
func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
	return v.BindFlagValues(pflagValueSet{flags})
}

// BindPFlag binds a specific key to a pflag (as used by cobra).
// Example (where serverCmd is a Cobra instance):
//
//	serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
//	Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }

// BindPFlag binds a specific key to a pflag (as used by cobra).
// Example (where serverCmd is a Cobra instance):
//
//	serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
//	Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
	if flag == nil {
		return fmt.Errorf("flag for %q is nil", key)
	}
	return v.BindFlagValue(key, pflagValue{flag})
}

// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long
// name as the config key.
func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) }

// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long
// name as the config key.
func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
	flags.VisitAll(func(flag FlagValue) {
		if err = v.BindFlagValue(flag.Name(), flag); err != nil {
			return
		}
	})
	return nil
}

// BindFlagValue binds a specific key to a FlagValue.
func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) }

// BindFlagValue binds a specific key to a FlagValue.
func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
	if flag == nil {
		return fmt.Errorf("flag for %q is nil", key)
	}
	v.pflags[strings.ToLower(key)] = flag
	return nil
}

// BindEnv binds a Viper key to a ENV variable.
// ENV variables are case sensitive.
// If only a key is provided, it will use the env key matching the key, uppercased.
// If more arguments are provided, they will represent the env variable names that
// should bind to this key and will be taken in the specified order.
// EnvPrefix will be used when set when env name is not provided.
func BindEnv(input ...string) error { return v.BindEnv(input...) }

// BindEnv binds a Viper key to a ENV variable.
// ENV variables are case sensitive.
// If only a key is provided, it will use the env key matching the key, uppercased.
// If more arguments are provided, they will represent the env variable names that
// should bind to this key and will be taken in the specified order.
// EnvPrefix will be used when set when env name is not provided.
func (v *Viper) BindEnv(input ...string) error {
	if len(input) == 0 {
		return fmt.Errorf("missing key to bind to")
	}

	key := strings.ToLower(input[0])

	if len(input) == 1 {
		v.env[key] = append(v.env[key], v.mergeWithEnvPrefix(key))
	} else {
		v.env[key] = append(v.env[key], input[1:]...)
	}

	return nil
}

// MustBindEnv wraps BindEnv in a panic.
// If there is an error binding an environment variable, MustBindEnv will
// panic.
func MustBindEnv(input ...string) { v.MustBindEnv(input...) }

// MustBindEnv wraps BindEnv in a panic.
// If there is an error binding an environment variable, MustBindEnv will
// panic.
func (v *Viper) MustBindEnv(input ...string) {
	if err := v.BindEnv(input...); err != nil {
		panic(fmt.Sprintf("error while binding environment variable: %v", err))
	}
}

// Given a key, find the value.
//
// Viper will check to see if an alias exists first.
// Viper will then check in the following order:
// flag, env, config file, key/value store.
// Lastly, if no value was found and flagDefault is true, and if the key
// corresponds to a flag, the flag's default value is returned.
//
// Note: this assumes a lower-cased key given.
func (v *Viper) find(lcaseKey string, flagDefault bool) any {
	var (
		val    any
		exists bool
		path   = strings.Split(lcaseKey, v.keyDelim)
		nested = len(path) > 1
	)

	// compute the path through the nested maps to the nested value
	if nested && v.isPathShadowedInDeepMap(path, castMapStringToMapInterface(v.aliases)) != "" {
		return nil
	}

	// if the requested key is an alias, then return the proper key
	lcaseKey = v.realKey(lcaseKey)
	path = strings.Split(lcaseKey, v.keyDelim)
	nested = len(path) > 1

	// Set() override first
	val = v.searchMap(v.override, path)
	if val != nil {
		return val
	}
	if nested && v.isPathShadowedInDeepMap(path, v.override) != "" {
		return nil
	}

	// PFlag override next
	flag, exists := v.pflags[lcaseKey]
	if exists && flag.HasChanged() {
		switch flag.ValueType() {
		case "int", "int8", "int16", "int32", "int64":
			return cast.ToInt(flag.ValueString())
		case "bool":
			return cast.ToBool(flag.ValueString())
		case "stringSlice", "stringArray":
			s := strings.TrimPrefix(flag.ValueString(), "[")
			s = strings.TrimSuffix(s, "]")
			res, _ := readAsCSV(s)
			return res
		case "boolSlice":
			s := strings.TrimPrefix(flag.ValueString(), "[")
			s = strings.TrimSuffix(s, "]")
			res, _ := readAsCSV(s)
			return cast.ToBoolSlice(res)
		case "intSlice":
			s := strings.TrimPrefix(flag.ValueString(), "[")
			s = strings.TrimSuffix(s, "]")
			res, _ := readAsCSV(s)
			return cast.ToIntSlice(res)
		case "uintSlice":
			s := strings.TrimPrefix(flag.ValueString(), "[")
			s = strings.TrimSuffix(s, "]")
			res, _ := readAsCSV(s)
			return cast.ToUintSlice(res)
		case "float64Slice":
			s := strings.TrimPrefix(flag.ValueString(), "[")
			s = strings.TrimSuffix(s, "]")
			res, _ := readAsCSV(s)
			return cast.ToFloat64Slice(res)
		case "durationSlice":
			s := strings.TrimPrefix(flag.ValueString(), "[")
			s = strings.TrimSuffix(s, "]")
			slice := strings.Split(s, ",")
			return cast.ToDurationSlice(slice)
		case "stringToString":
			return stringToStringConv(flag.ValueString())
		case "stringToInt":
			return stringToIntConv(flag.ValueString())
		default:
			return flag.ValueString()
		}
	}
	if nested && v.isPathShadowedInFlatMap(path, v.pflags) != "" {
		return nil
	}

	// Env override next
	if v.automaticEnvApplied {
		envKey := strings.Join(append(v.parents, lcaseKey), ".")
		// even if it hasn't been registered, if automaticEnv is used,
		// check any Get request
		if val, ok := v.getEnv(v.mergeWithEnvPrefix(envKey)); ok {
			return val
		}
		if nested && v.isPathShadowedInAutoEnv(path) != "" {
			return nil
		}
	}
	envkeys, exists := v.env[lcaseKey]
	if exists {
		for _, envkey := range envkeys {
			if val, ok := v.getEnv(envkey); ok {
				return val
			}
		}
	}
	if nested && v.isPathShadowedInFlatMap(path, v.env) != "" {
		return nil
	}

	// Config file next
	val = v.searchIndexableWithPathPrefixes(v.config, path)
	if val != nil {
		return val
	}
	if nested && v.isPathShadowedInDeepMap(path, v.config) != "" {
		return nil
	}

	// K/V store next
	val = v.searchMap(v.kvstore, path)
	if val != nil {
		return val
	}
	if nested && v.isPathShadowedInDeepMap(path, v.kvstore) != "" {
		return nil
	}

	// Default next
	val = v.searchMap(v.defaults, path)
	if val != nil {
		return val
	}
	if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" {
		return nil
	}

	if flagDefault {
		// last chance: if no value is found and a flag does exist for the key,
		// get the flag's default value even if the flag's value has not been set.
		if flag, exists := v.pflags[lcaseKey]; exists {
			switch flag.ValueType() {
			case "int", "int8", "int16", "int32", "int64":
				return cast.ToInt(flag.ValueString())
			case "bool":
				return cast.ToBool(flag.ValueString())
			case "stringSlice", "stringArray":
				s := strings.TrimPrefix(flag.ValueString(), "[")
				s = strings.TrimSuffix(s, "]")
				res, _ := readAsCSV(s)
				return res
			case "boolSlice":
				s := strings.TrimPrefix(flag.ValueString(), "[")
				s = strings.TrimSuffix(s, "]")
				res, _ := readAsCSV(s)
				return cast.ToBoolSlice(res)
			case "intSlice":
				s := strings.TrimPrefix(flag.ValueString(), "[")
				s = strings.TrimSuffix(s, "]")
				res, _ := readAsCSV(s)
				return cast.ToIntSlice(res)
			case "uintSlice":
				s := strings.TrimPrefix(flag.ValueString(), "[")
				s = strings.TrimSuffix(s, "]")
				res, _ := readAsCSV(s)
				return cast.ToUintSlice(res)
			case "float64Slice":
				s := strings.TrimPrefix(flag.ValueString(), "[")
				s = strings.TrimSuffix(s, "]")
				res, _ := readAsCSV(s)
				return cast.ToFloat64Slice(res)
			case "stringToString":
				return stringToStringConv(flag.ValueString())
			case "stringToInt":
				return stringToIntConv(flag.ValueString())
			case "durationSlice":
				s := strings.TrimPrefix(flag.ValueString(), "[")
				s = strings.TrimSuffix(s, "]")
				slice := strings.Split(s, ",")
				return cast.ToDurationSlice(slice)
			default:
				return flag.ValueString()
			}
		}
		// last item, no need to check shadowing
	}

	return nil
}

func readAsCSV(val string) ([]string, error) {
	if val == "" {
		return []string{}, nil
	}
	stringReader := strings.NewReader(val)
	csvReader := csv.NewReader(stringReader)
	return csvReader.Read()
}

// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/master/string_to_string.go#L79
// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap.
func stringToStringConv(val string) any {
	val = strings.Trim(val, "[]")
	// An empty string would cause an empty map
	if val == "" {
		return map[string]any{}
	}
	r := csv.NewReader(strings.NewReader(val))
	ss, err := r.Read()
	if err != nil {
		return nil
	}
	out := make(map[string]any, len(ss))
	for _, pair := range ss {
		k, vv, found := strings.Cut(pair, "=")
		if !found {
			return nil
		}
		out[k] = vv
	}
	return out
}

// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/d5e0c0615acee7028e1e2740a11102313be88de1/string_to_int.go#L68
// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap.
func stringToIntConv(val string) any {
	val = strings.Trim(val, "[]")
	// An empty string would cause an empty map
	if val == "" {
		return map[string]any{}
	}
	ss := strings.Split(val, ",")
	out := make(map[string]any, len(ss))
	for _, pair := range ss {
		k, vv, found := strings.Cut(pair, "=")
		if !found {
			return nil
		}
		var err error
		out[k], err = strconv.Atoi(vv)
		if err != nil {
			return nil
		}
	}
	return out
}

// IsSet checks to see if the key has been set in any of the data locations.
// IsSet is case-insensitive for a key.
func IsSet(key string) bool { return v.IsSet(key) }

// IsSet checks to see if the key has been set in any of the data locations.
// IsSet is case-insensitive for a key.
func (v *Viper) IsSet(key string) bool {
	lcaseKey := strings.ToLower(key)
	val := v.find(lcaseKey, false)
	return val != nil
}

// AutomaticEnv makes Viper check if environment variables match any of the existing keys
// (config, default or flags). If matching env vars are found, they are loaded into Viper.
func AutomaticEnv() { v.AutomaticEnv() }

// AutomaticEnv makes Viper check if environment variables match any of the existing keys
// (config, default or flags). If matching env vars are found, they are loaded into Viper.
func (v *Viper) AutomaticEnv() {
	v.automaticEnvApplied = true
}

// SetEnvKeyReplacer sets the strings.Replacer on the viper object
// Useful for mapping an environmental variable to a key that does
// not match it.
func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) }

// SetEnvKeyReplacer sets the strings.Replacer on the viper object
// Useful for mapping an environmental variable to a key that does
// not match it.
func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
	v.envKeyReplacer = r
}

// RegisterAlias creates an alias that provides another accessor for the same key.
// This enables one to change a name without breaking the application.
func RegisterAlias(alias, key string) { v.RegisterAlias(alias, key) }

// RegisterAlias creates an alias that provides another accessor for the same key.
// This enables one to change a name without breaking the application.
func (v *Viper) RegisterAlias(alias, key string) {
	v.registerAlias(alias, strings.ToLower(key))
}

func (v *Viper) registerAlias(alias, key string) {
	alias = strings.ToLower(alias)
Download .txt
gitextract_5z5ozi0n/

├── .editorconfig
├── .envrc
├── .github/
│   ├── .editorconfig
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yaml
│   │   ├── config.yml
│   │   └── feature_request.yaml
│   ├── PULL_REQUEST_TEMPLATES.md
│   ├── dependabot.yaml
│   ├── octoslash/
│   │   ├── policies/
│   │   │   ├── collaborator.cedar
│   │   │   └── triager.cedar
│   │   └── principals.json
│   ├── release.yml
│   └── workflows/
│       ├── checks.yaml
│       ├── ci.yaml
│       ├── octoslash.yaml
│       └── stale.yaml
├── .gitignore
├── .golangci.yaml
├── .yamlignore
├── .yamllint.yaml
├── LICENSE
├── Makefile
├── README.md
├── TROUBLESHOOTING.md
├── UPGRADE.md
├── encoding.go
├── encoding_test.go
├── errors.go
├── experimental.go
├── file.go
├── finder.go
├── finder_example_test.go
├── finder_test.go
├── flags.go
├── flags_test.go
├── flake.nix
├── go.mod
├── go.sum
├── internal/
│   ├── encoding/
│   │   ├── dotenv/
│   │   │   ├── codec.go
│   │   │   ├── codec_test.go
│   │   │   └── map_utils.go
│   │   ├── json/
│   │   │   ├── codec.go
│   │   │   └── codec_test.go
│   │   ├── toml/
│   │   │   ├── codec.go
│   │   │   └── codec_test.go
│   │   └── yaml/
│   │       ├── codec.go
│   │       └── codec_test.go
│   ├── features/
│   │   ├── bind_struct.go
│   │   ├── bind_struct_default.go
│   │   ├── finder.go
│   │   └── finder_default.go
│   └── testutil/
│       └── filepath.go
├── logger.go
├── overrides_test.go
├── remote/
│   ├── go.mod
│   ├── go.sum
│   └── remote.go
├── remote.go
├── util.go
├── util_test.go
├── viper.go
├── viper_test.go
└── viper_yaml_test.go
Download .txt
SYMBOL INDEX (436 symbols across 32 files)

FILE: encoding.go
  type Encoder (line 16) | type Encoder interface
  type Decoder (line 22) | type Decoder interface
  type Codec (line 27) | type Codec interface
  type EncoderRegistry (line 39) | type EncoderRegistry interface
  type DecoderRegistry (line 48) | type DecoderRegistry interface
  type CodecRegistry (line 53) | type CodecRegistry interface
  function WithEncoderRegistry (line 59) | func WithEncoderRegistry(r EncoderRegistry) Option {
  function WithDecoderRegistry (line 70) | func WithDecoderRegistry(r DecoderRegistry) Option {
  function WithCodecRegistry (line 81) | func WithCodecRegistry(r CodecRegistry) Option {
  type DefaultCodecRegistry (line 93) | type DefaultCodecRegistry struct
    method init (line 109) | func (r *DefaultCodecRegistry) init() {
    method RegisterCodec (line 118) | func (r *DefaultCodecRegistry) RegisterCodec(format string, codec Code...
    method Encoder (line 132) | func (r *DefaultCodecRegistry) Encoder(format string) (Encoder, error) {
    method Decoder (line 144) | func (r *DefaultCodecRegistry) Decoder(format string) (Decoder, error) {
    method codec (line 153) | func (r *DefaultCodecRegistry) codec(format string) (Codec, bool) {
  function NewCodecRegistry (line 101) | func NewCodecRegistry() *DefaultCodecRegistry {

FILE: encoding_test.go
  type codec (line 10) | type codec struct
    method Encode (line 12) | func (codec) Encode(_ map[string]any) ([]byte, error) {
    method Decode (line 16) | func (codec) Decode(_ []byte, _ map[string]any) error {
  function TestDefaultCodecRegistry (line 20) | func TestDefaultCodecRegistry(t *testing.T) {

FILE: errors.go
  type FileLookupError (line 11) | type FileLookupError interface
  type ConfigFileNotFoundError (line 20) | type ConfigFileNotFoundError struct
    method Error (line 26) | func (e ConfigFileNotFoundError) Error() string {
    method Unwrap (line 31) | func (e ConfigFileNotFoundError) Unwrap() error {
  type FileNotFoundFromSearchError (line 37) | type FileNotFoundFromSearchError struct
    method fileLookup (line 42) | func (e FileNotFoundFromSearchError) fileLookup() {}
    method Error (line 45) | func (e FileNotFoundFromSearchError) Error() string {
  type FileNotFoundError (line 56) | type FileNotFoundError struct
    method fileLookup (line 61) | func (e FileNotFoundError) fileLookup() {}
    method Error (line 64) | func (e FileNotFoundError) Error() string {
  type ConfigFileAlreadyExistsError (line 69) | type ConfigFileAlreadyExistsError
    method Error (line 72) | func (e ConfigFileAlreadyExistsError) Error() string {
  type ConfigMarshalError (line 77) | type ConfigMarshalError struct
    method Error (line 82) | func (e ConfigMarshalError) Error() string {
  type UnsupportedConfigError (line 88) | type UnsupportedConfigError
    method Error (line 91) | func (str UnsupportedConfigError) Error() string {

FILE: experimental.go
  function ExperimentalBindStruct (line 4) | func ExperimentalBindStruct() Option {

FILE: file.go
  function ExperimentalFinder (line 12) | func ExperimentalFinder() Option {
  method findConfigFile (line 19) | func (v *Viper) findConfigFile() (string, error) {
  method findConfigFileWithFinder (line 45) | func (v *Viper) findConfigFileWithFinder(finder Finder) (string, error) {
  method findConfigFileOld (line 62) | func (v *Viper) findConfigFileOld() (string, error) {
  method searchInPath (line 74) | func (v *Viper) searchInPath(in string) (filename string) {
  function exists (line 94) | func exists(fs afero.Fs, path string) (bool, error) {

FILE: finder.go
  function WithFinder (line 10) | func WithFinder(f Finder) Option {
  type Finder (line 21) | type Finder interface
  function Finders (line 26) | func Finders(finders ...Finder) Finder {
  type combinedFinder (line 31) | type combinedFinder struct
    method Find (line 36) | func (c *combinedFinder) Find(fsys afero.Fs) ([]string, error) {

FILE: finder_example_test.go
  function ExampleFinder (line 12) | func ExampleFinder() {
  function ExampleFinders (line 40) | func ExampleFinders() {

FILE: finder_test.go
  type finderStub (line 11) | type finderStub struct
    method Find (line 15) | func (f finderStub) Find(_ afero.Fs) ([]string, error) {
  function TestFinders (line 19) | func TestFinders(t *testing.T) {

FILE: flags.go
  type FlagValueSet (line 7) | type FlagValueSet interface
  type FlagValue (line 13) | type FlagValue interface
  type pflagValueSet (line 22) | type pflagValueSet struct
    method VisitAll (line 27) | func (p pflagValueSet) VisitAll(fn func(flag FlagValue)) {
  type pflagValue (line 35) | type pflagValue struct
    method HasChanged (line 40) | func (p pflagValue) HasChanged() bool {
    method Name (line 45) | func (p pflagValue) Name() string {
    method ValueString (line 50) | func (p pflagValue) ValueString() string {
    method ValueType (line 55) | func (p pflagValue) ValueType() string {

FILE: flags_test.go
  function TestBindFlagValueSet (line 11) | func TestBindFlagValueSet(t *testing.T) {
  function TestBindFlagValue (line 46) | func TestBindFlagValue(t *testing.T) {

FILE: internal/encoding/dotenv/codec.go
  constant keyDelimiter (line 12) | keyDelimiter = "_"
  type Codec (line 16) | type Codec struct
    method Encode (line 19) | func (Codec) Encode(v map[string]any) ([]byte, error) {
    method Decode (line 45) | func (Codec) Decode(b []byte, v map[string]any) error {

FILE: internal/encoding/dotenv/codec_test.go
  constant original (line 11) | original = `# key-value pair
  constant encoded (line 16) | encoded = `KEY=value
  function TestCodec_Encode (line 24) | func TestCodec_Encode(t *testing.T) {
  function TestCodec_Decode (line 33) | func TestCodec_Decode(t *testing.T) {

FILE: internal/encoding/dotenv/map_utils.go
  function flattenAndMergeMap (line 12) | func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter stri...

FILE: internal/encoding/json/codec.go
  type Codec (line 8) | type Codec struct
    method Encode (line 11) | func (Codec) Encode(v map[string]any) ([]byte, error) {
    method Decode (line 17) | func (Codec) Decode(b []byte, v map[string]any) error {

FILE: internal/encoding/json/codec_test.go
  constant encoded (line 11) | encoded = `{
  function TestCodec_Encode (line 56) | func TestCodec_Encode(t *testing.T) {
  function TestCodec_Decode (line 65) | func TestCodec_Decode(t *testing.T) {

FILE: internal/encoding/toml/codec.go
  type Codec (line 8) | type Codec struct
    method Encode (line 11) | func (Codec) Encode(v map[string]any) ([]byte, error) {
    method Decode (line 16) | func (Codec) Decode(b []byte, v map[string]any) error {

FILE: internal/encoding/toml/codec_test.go
  constant original (line 11) | original = `# key-value pair
  constant encoded (line 31) | encoded = `key = 'value'
  function TestCodec_Encode (line 66) | func TestCodec_Encode(t *testing.T) {
  function TestCodec_Decode (line 75) | func TestCodec_Decode(t *testing.T) {

FILE: internal/encoding/yaml/codec.go
  type Codec (line 6) | type Codec struct
    method Encode (line 9) | func (Codec) Encode(v map[string]any) ([]byte, error) {
    method Decode (line 14) | func (Codec) Decode(b []byte, v map[string]any) error {

FILE: internal/encoding/yaml/codec_test.go
  constant original (line 11) | original = `# key-value pair
  constant encoded (line 32) | encoded = `key: value
  function TestCodec_Encode (line 97) | func TestCodec_Encode(t *testing.T) {
  function TestCodec_Decode (line 106) | func TestCodec_Decode(t *testing.T) {

FILE: internal/features/bind_struct.go
  constant BindStruct (line 6) | BindStruct = true

FILE: internal/features/bind_struct_default.go
  constant BindStruct (line 6) | BindStruct = false

FILE: internal/features/finder.go
  constant Finder (line 6) | Finder = true

FILE: internal/features/finder_default.go
  constant Finder (line 6) | Finder = false

FILE: internal/testutil/filepath.go
  function AbsFilePath (line 9) | func AbsFilePath(t *testing.T, path string) string {

FILE: logger.go
  function WithLogger (line 9) | func WithLogger(l *slog.Logger) Option {
  type discardHandler (line 15) | type discardHandler struct
    method Enabled (line 17) | func (n *discardHandler) Enabled(_ context.Context, _ slog.Level) bool {
    method Handle (line 21) | func (n *discardHandler) Handle(_ context.Context, _ slog.Record) error {
    method WithAttrs (line 25) | func (n *discardHandler) WithAttrs(_ []slog.Attr) slog.Handler {
    method WithGroup (line 29) | func (n *discardHandler) WithGroup(_ string) slog.Handler {

FILE: overrides_test.go
  type layer (line 11) | type layer
  constant defaultLayer (line 14) | defaultLayer layer = iota + 1
  constant overrideLayer (line 15) | overrideLayer
  function TestNestedOverrides (line 18) | func TestNestedOverrides(t *testing.T) {
  function overrideDefault (line 77) | func overrideDefault(assert *assert.Assertions, firstPath string, firstV...
  function override (line 81) | func override(assert *assert.Assertions, firstPath string, firstValue an...
  function overrideFromLayer (line 96) | func overrideFromLayer(l layer, assert *assert.Assertions, firstPath str...
  function deepCheckValue (line 130) | func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys [...

FILE: remote.go
  function resetRemote (line 14) | func resetRemote() {
  type remoteConfigFactory (line 18) | type remoteConfigFactory interface
  type RemoteResponse (line 25) | type RemoteResponse struct
  type UnsupportedRemoteProviderError (line 35) | type UnsupportedRemoteProviderError
    method Error (line 38) | func (str UnsupportedRemoteProviderError) Error() string {
  type RemoteConfigError (line 44) | type RemoteConfigError
    method Error (line 47) | func (rce RemoteConfigError) Error() string {
  type defaultRemoteProvider (line 51) | type defaultRemoteProvider struct
    method Provider (line 58) | func (rp defaultRemoteProvider) Provider() string {
    method Endpoint (line 62) | func (rp defaultRemoteProvider) Endpoint() string {
    method Path (line 66) | func (rp defaultRemoteProvider) Path() string {
    method SecretKeyring (line 70) | func (rp defaultRemoteProvider) SecretKeyring() string {
  type RemoteProvider (line 78) | type RemoteProvider interface
  function AddRemoteProvider (line 93) | func AddRemoteProvider(provider, endpoint, path string) error {
  method AddRemoteProvider (line 105) | func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
  function AddSecureRemoteProvider (line 134) | func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring str...
  method AddSecureRemoteProvider (line 148) | func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secret...
  method providerPathExists (line 168) | func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
  function ReadRemoteConfig (line 179) | func ReadRemoteConfig() error { return v.ReadRemoteConfig() }
  method ReadRemoteConfig (line 183) | func (v *Viper) ReadRemoteConfig() error {
  function WatchRemoteConfig (line 188) | func WatchRemoteConfig() error { return v.WatchRemoteConfig() }
  method WatchRemoteConfig (line 191) | func (v *Viper) WatchRemoteConfig() error {
  method WatchRemoteConfigOnChannel (line 196) | func (v *Viper) WatchRemoteConfigOnChannel() error {
  method getKeyValueConfig (line 201) | func (v *Viper) getKeyValueConfig() error {
  method getRemoteConfig (line 225) | func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]any...
  method watchKeyValueConfigOnChannel (line 235) | func (v *Viper) watchKeyValueConfigOnChannel() error {
  method watchKeyValueConfig (line 259) | func (v *Viper) watchKeyValueConfig() error {
  method watchRemoteConfig (line 277) | func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]a...

FILE: remote/remote.go
  type remoteConfigProvider (line 20) | type remoteConfigProvider struct
    method Get (line 22) | func (rc remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader...
    method Watch (line 34) | func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Read...
    method WatchChannel (line 47) | func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (...
  function getConfigManager (line 75) | func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, err...
  function init (line 119) | func init() {

FILE: util.go
  type ConfigParseError (line 26) | type ConfigParseError struct
    method Error (line 31) | func (pe ConfigParseError) Error() string {
    method Unwrap (line 36) | func (pe ConfigParseError) Unwrap() error {
  function toCaseInsensitiveValue (line 42) | func toCaseInsensitiveValue(value any) any {
  function copyAndInsensitiviseMap (line 55) | func copyAndInsensitiviseMap(m map[string]any) map[string]any {
  function insensitiviseVal (line 73) | func insensitiviseVal(val any) any {
  function insensitiviseMap (line 89) | func insensitiviseMap(m map[string]any) {
  function insensitiveArray (line 102) | func insensitiveArray(a []any) {
  function absPathify (line 108) | func absPathify(logger *slog.Logger, inPath string) string {
  function userHomeDir (line 131) | func userHomeDir() string {
  function safeMul (line 142) | func safeMul(a, b uint) uint {
  function parseSizeInBytes (line 151) | func parseSizeInBytes(sizeStr string) uint {
  function deepSearch (line 189) | func deepSearch(m map[string]any, path []string) map[string]any {

FILE: util_test.go
  function TestCopyAndInsensitiviseMap (line 22) | func TestCopyAndInsensitiviseMap(t *testing.T) {
  function TestAbsPathify (line 53) | func TestAbsPathify(t *testing.T) {

FILE: viper.go
  function init (line 50) | func init() {
  type DecoderConfigOption (line 56) | type DecoderConfigOption
  function DecodeHook (line 65) | func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
  type Viper (line 107) | type Viper struct
    method OnConfigChange (line 274) | func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
    method WatchConfig (line 282) | func (v *Viper) WatchConfig() {
    method SetConfigFile (line 361) | func (v *Viper) SetConfigFile(in string) {
    method SetEnvPrefix (line 375) | func (v *Viper) SetEnvPrefix(in string) {
    method GetEnvPrefix (line 385) | func (v *Viper) GetEnvPrefix() string {
    method mergeWithEnvPrefix (line 389) | func (v *Viper) mergeWithEnvPrefix(in string) string {
    method AllowEmptyEnv (line 405) | func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) {
    method getEnv (line 416) | func (v *Viper) getEnv(key string) (string, bool) {
    method ConfigFileUsed (line 430) | func (v *Viper) ConfigFileUsed() string { return v.configFile }
    method AddConfigPath (line 438) | func (v *Viper) AddConfigPath(in string) {
    method searchMap (line 456) | func (v *Viper) searchMap(source map[string]any, path []string) any {
    method searchIndexableWithPathPrefixes (line 495) | func (v *Viper) searchIndexableWithPathPrefixes(source any, path []str...
    method searchSliceWithPathPrefixes (line 524) | func (v *Viper) searchSliceWithPathPrefixes(
    method searchMapWithPathPrefixes (line 560) | func (v *Viper) searchMapWithPathPrefixes(
    method isPathShadowedInDeepMap (line 595) | func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]an...
    method isPathShadowedInFlatMap (line 621) | func (v *Viper) isPathShadowedInFlatMap(path []string, mi any) string {
    method isPathShadowedInAutoEnv (line 649) | func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
    method SetTypeByDefaultValue (line 690) | func (v *Viper) SetTypeByDefaultValue(enable bool) {
    method Get (line 715) | func (v *Viper) Get(key string) any {
    method Sub (line 770) | func (v *Viper) Sub(key string) *Viper {
    method GetString (line 794) | func (v *Viper) GetString(key string) string {
    method GetBool (line 802) | func (v *Viper) GetBool(key string) bool {
    method GetInt (line 810) | func (v *Viper) GetInt(key string) int {
    method GetInt32 (line 818) | func (v *Viper) GetInt32(key string) int32 {
    method GetInt64 (line 826) | func (v *Viper) GetInt64(key string) int64 {
    method GetUint8 (line 834) | func (v *Viper) GetUint8(key string) uint8 {
    method GetUint (line 842) | func (v *Viper) GetUint(key string) uint {
    method GetUint16 (line 850) | func (v *Viper) GetUint16(key string) uint16 {
    method GetUint32 (line 858) | func (v *Viper) GetUint32(key string) uint32 {
    method GetUint64 (line 866) | func (v *Viper) GetUint64(key string) uint64 {
    method GetFloat64 (line 874) | func (v *Viper) GetFloat64(key string) float64 {
    method GetTime (line 882) | func (v *Viper) GetTime(key string) time.Time {
    method GetDuration (line 890) | func (v *Viper) GetDuration(key string) time.Duration {
    method GetIntSlice (line 898) | func (v *Viper) GetIntSlice(key string) []int {
    method GetStringSlice (line 906) | func (v *Viper) GetStringSlice(key string) []string {
    method GetStringMap (line 914) | func (v *Viper) GetStringMap(key string) map[string]any {
    method GetStringMapString (line 922) | func (v *Viper) GetStringMapString(key string) map[string]string {
    method GetStringMapStringSlice (line 930) | func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {
    method GetSizeInBytes (line 940) | func (v *Viper) GetSizeInBytes(key string) uint {
    method UnmarshalKey (line 951) | func (v *Viper) UnmarshalKey(key string, rawVal any, opts ...DecoderCo...
    method Unmarshal (line 963) | func (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) err...
    method decodeStructKeys (line 980) | func (v *Viper) decodeStructKeys(input any, opts ...DecoderConfigOptio...
    method defaultDecoderConfig (line 1000) | func (v *Viper) defaultDecoderConfig(output any, opts ...DecoderConfig...
    method UnmarshalExact (line 1065) | func (v *Viper) UnmarshalExact(rawVal any, opts ...DecoderConfigOption...
    method BindPFlags (line 1091) | func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
    method BindPFlag (line 1107) | func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
    method BindFlagValues (line 1120) | func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
    method BindFlagValue (line 1133) | func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
    method BindEnv (line 1155) | func (v *Viper) BindEnv(input ...string) error {
    method MustBindEnv (line 1179) | func (v *Viper) MustBindEnv(input ...string) {
    method find (line 1194) | func (v *Viper) find(lcaseKey string, flagDefault bool) any {
    method IsSet (line 1438) | func (v *Viper) IsSet(key string) bool {
    method AutomaticEnv (line 1450) | func (v *Viper) AutomaticEnv() {
    method SetEnvKeyReplacer (line 1462) | func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
    method RegisterAlias (line 1472) | func (v *Viper) RegisterAlias(alias, key string) {
    method registerAlias (line 1476) | func (v *Viper) registerAlias(alias, key string) {
    method realKey (line 1508) | func (v *Viper) realKey(key string) string {
    method InConfig (line 1522) | func (v *Viper) InConfig(key string) bool {
    method SetDefault (line 1540) | func (v *Viper) SetDefault(key string, value any) {
    method Set (line 1563) | func (v *Viper) Set(key string, value any) {
    method ReadInConfig (line 1582) | func (v *Viper) ReadInConfig() error {
    method MergeInConfig (line 1619) | func (v *Viper) MergeInConfig() error {
    method ReadConfig (line 1644) | func (v *Viper) ReadConfig(in io.Reader) error {
    method MergeConfig (line 1661) | func (v *Viper) MergeConfig(in io.Reader) error {
    method MergeConfigMap (line 1677) | func (v *Viper) MergeConfigMap(cfg map[string]any) error {
    method WriteConfig (line 1690) | func (v *Viper) WriteConfig() error {
    method SafeWriteConfig (line 1702) | func (v *Viper) SafeWriteConfig() error {
    method WriteConfigAs (line 1713) | func (v *Viper) WriteConfigAs(filename string) error {
    method WriteConfigTo (line 1721) | func (v *Viper) WriteConfigTo(w io.Writer) error {
    method SafeWriteConfigAs (line 1735) | func (v *Viper) SafeWriteConfigAs(filename string) error {
    method writeConfig (line 1743) | func (v *Viper) writeConfig(filename string, force bool) error {
    method unmarshalReader (line 1781) | func (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error {
    method marshalWriter (line 1815) | func (v *Viper) marshalWriter(w io.Writer, configType string) error {
    method AllKeys (line 1970) | func (v *Viper) AllKeys() []string {
    method flattenAndMergeMap (line 1996) | func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[strin...
    method mergeFlatMap (line 2029) | func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]any)...
    method AllSettings (line 2053) | func (v *Viper) AllSettings() map[string]any {
    method getSettings (line 2057) | func (v *Viper) getSettings(keys []string) map[string]any {
    method SetFs (line 2080) | func (v *Viper) SetFs(fs afero.Fs) {
    method SetConfigName (line 2090) | func (v *Viper) SetConfigName(in string) {
    method SetConfigType (line 2107) | func (v *Viper) SetConfigType(in string) {
    method SetConfigPermissions (line 2117) | func (v *Viper) SetConfigPermissions(perm os.FileMode) {
    method getConfigType (line 2121) | func (v *Viper) getConfigType() string {
    method getConfigFile (line 2140) | func (v *Viper) getConfigFile() (string, error) {
    method Debug (line 2159) | func (v *Viper) Debug() { v.DebugTo(os.Stdout) }
    method DebugTo (line 2162) | func (v *Viper) DebugTo(w io.Writer) {
  function New (line 158) | func New() *Viper {
  type Option (line 190) | type Option interface
  type optionFunc (line 194) | type optionFunc
    method apply (line 196) | func (fn optionFunc) apply(v *Viper) {
  function KeyDelimiter (line 202) | func KeyDelimiter(d string) Option {
  type StringReplacer (line 209) | type StringReplacer interface
  function EnvKeyReplacer (line 215) | func EnvKeyReplacer(r StringReplacer) Option {
  function WithDecodeHook (line 226) | func WithDecodeHook(h mapstructure.DecodeHookFunc) Option {
  function NewWithOptions (line 237) | func NewWithOptions(opts ...Option) *Viper {
  function SetOptions (line 251) | func SetOptions(opts ...Option) {
  function Reset (line 260) | func Reset() {
  function OnConfigChange (line 271) | func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
  function WatchConfig (line 279) | func WatchConfig() { v.WatchConfig() }
  function SetConfigFile (line 357) | func SetConfigFile(in string) { v.SetConfigFile(in) }
  function SetEnvPrefix (line 370) | func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
  function GetEnvPrefix (line 382) | func GetEnvPrefix() string { return v.GetEnvPrefix() }
  function AllowEmptyEnv (line 400) | func AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) }
  function ConfigFileUsed (line 427) | func ConfigFileUsed() string { return v.ConfigFileUsed() }
  function AddConfigPath (line 434) | func AddConfigPath(in string) { v.AddConfigPath(in) }
  function SetTypeByDefaultValue (line 674) | func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
  function GetViper (line 695) | func GetViper() *Viper {
  function Get (line 706) | func Get(key string) any { return v.Get(key) }
  function Sub (line 766) | func Sub(key string) *Viper { return v.Sub(key) }
  function GetString (line 791) | func GetString(key string) string { return v.GetString(key) }
  function GetBool (line 799) | func GetBool(key string) bool { return v.GetBool(key) }
  function GetInt (line 807) | func GetInt(key string) int { return v.GetInt(key) }
  function GetInt32 (line 815) | func GetInt32(key string) int32 { return v.GetInt32(key) }
  function GetInt64 (line 823) | func GetInt64(key string) int64 { return v.GetInt64(key) }
  function GetUint8 (line 831) | func GetUint8(key string) uint8 { return v.GetUint8(key) }
  function GetUint (line 839) | func GetUint(key string) uint { return v.GetUint(key) }
  function GetUint16 (line 847) | func GetUint16(key string) uint16 { return v.GetUint16(key) }
  function GetUint32 (line 855) | func GetUint32(key string) uint32 { return v.GetUint32(key) }
  function GetUint64 (line 863) | func GetUint64(key string) uint64 { return v.GetUint64(key) }
  function GetFloat64 (line 871) | func GetFloat64(key string) float64 { return v.GetFloat64(key) }
  function GetTime (line 879) | func GetTime(key string) time.Time { return v.GetTime(key) }
  function GetDuration (line 887) | func GetDuration(key string) time.Duration { return v.GetDuration(key) }
  function GetIntSlice (line 895) | func GetIntSlice(key string) []int { return v.GetIntSlice(key) }
  function GetStringSlice (line 903) | func GetStringSlice(key string) []string { return v.GetStringSlice(key) }
  function GetStringMap (line 911) | func GetStringMap(key string) map[string]any { return v.GetStringMap(key) }
  function GetStringMapString (line 919) | func GetStringMapString(key string) map[string]string { return v.GetStri...
  function GetStringMapStringSlice (line 927) | func GetStringMapStringSlice(key string) map[string][]string { return v....
  function GetSizeInBytes (line 936) | func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) }
  function UnmarshalKey (line 946) | func UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) e...
  function Unmarshal (line 957) | func Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
  function stringToWeakSliceHookFunc (line 1029) | func stringToWeakSliceHookFunc(sep string) mapstructure.DecodeHookFunc {
  function decode (line 1049) | func decode(input any, config *mapstructure.DecoderConfig) error {
  function UnmarshalExact (line 1059) | func UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {
  function BindPFlags (line 1087) | func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) }
  function BindPFlag (line 1100) | func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(...
  function BindFlagValues (line 1116) | func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(...
  function BindFlagValue (line 1130) | func BindFlagValue(key string, flag FlagValue) error { return v.BindFlag...
  function BindEnv (line 1147) | func BindEnv(input ...string) error { return v.BindEnv(input...) }
  function MustBindEnv (line 1174) | func MustBindEnv(input ...string) { v.MustBindEnv(input...) }
  function readAsCSV (line 1375) | func readAsCSV(val string) ([]string, error) {
  function stringToStringConv (line 1386) | func stringToStringConv(val string) any {
  function stringToIntConv (line 1410) | func stringToIntConv(val string) any {
  function IsSet (line 1434) | func IsSet(key string) bool { return v.IsSet(key) }
  function AutomaticEnv (line 1446) | func AutomaticEnv() { v.AutomaticEnv() }
  function SetEnvKeyReplacer (line 1457) | func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) }
  function RegisterAlias (line 1468) | func RegisterAlias(alias, key string) { v.RegisterAlias(alias, key) }
  function InConfig (line 1519) | func InConfig(key string) bool { return v.InConfig(key) }
  function SetDefault (line 1535) | func SetDefault(key string, value any) { v.SetDefault(key, value) }
  function Set (line 1557) | func Set(key string, value any) { v.Set(key, value) }
  function ReadInConfig (line 1578) | func ReadInConfig() error { return v.ReadInConfig() }
  function MergeInConfig (line 1616) | func MergeInConfig() error { return v.MergeInConfig() }
  function ReadConfig (line 1640) | func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
  function MergeConfig (line 1658) | func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
  function MergeConfigMap (line 1673) | func MergeConfigMap(cfg map[string]any) error { return v.MergeConfigMap(...
  function WriteConfig (line 1687) | func WriteConfig() error { return v.WriteConfig() }
  function SafeWriteConfig (line 1699) | func SafeWriteConfig() error { return v.SafeWriteConfig() }
  function WriteConfigAs (line 1710) | func WriteConfigAs(filename string) error { return v.WriteConfigAs(filen...
  function WriteConfigTo (line 1718) | func WriteConfigTo(w io.Writer) error { return v.WriteConfigTo(w) }
  function SafeWriteConfigAs (line 1732) | func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfig...
  function keyExists (line 1836) | func keyExists(k string, m map[string]any) string {
  function castToMapStringInterface (line 1847) | func castToMapStringInterface(
  function castMapStringSliceToMapInterface (line 1857) | func castMapStringSliceToMapInterface(src map[string][]string) map[strin...
  function castMapStringToMapInterface (line 1865) | func castMapStringToMapInterface(src map[string]string) map[string]any {
  function castMapFlagToMapInterface (line 1873) | func castMapFlagToMapInterface(src map[string]FlagValue) map[string]any {
  function mergeMaps (line 1886) | func mergeMaps(src, tgt map[string]any, itgt map[any]any) {
  function AllKeys (line 1966) | func AllKeys() []string { return v.AllKeys() }
  function AllSettings (line 2050) | func AllSettings() map[string]any { return v.AllSettings() }
  function SetFs (line 2077) | func SetFs(fs afero.Fs) { v.SetFs(fs) }
  function SetConfigName (line 2086) | func SetConfigName(in string) { v.SetConfigName(in) }
  function SetConfigType (line 2103) | func SetConfigType(in string) { v.SetConfigType(in) }
  function SetConfigPermissions (line 2114) | func SetConfigPermissions(perm os.FileMode) { v.SetConfigPermissions(per...
  function Debug (line 2153) | func Debug() { v.Debug() }
  function DebugTo (line 2156) | func DebugTo(w io.Writer) { v.DebugTo(w) }

FILE: viper_test.go
  type testUnmarshalExtra (line 55) | type testUnmarshalExtra struct
  function initConfigs (line 93) | func initConfigs(v *Viper) {
  function initConfig (line 116) | func initConfig(typ, config string, v *Viper) {
  function initDirs (line 126) | func initDirs(t *testing.T) (string, string) {
  type stringValue (line 154) | type stringValue
    method Set (line 161) | func (s *stringValue) Set(val string) error {
    method Type (line 166) | func (s *stringValue) Type() string {
    method String (line 170) | func (s *stringValue) String() string {
  function newStringValue (line 156) | func newStringValue(val string, p *string) *stringValue {
  function TestGetConfigFile (line 174) | func TestGetConfigFile(t *testing.T) {
  function TestReadInConfig (line 358) | func TestReadInConfig(t *testing.T) {
  function TestDefault (line 469) | func TestDefault(t *testing.T) {
  function TestUnmarshaling (line 484) | func TestUnmarshaling(t *testing.T) {
  function TestUnmarshalExact (line 500) | func TestUnmarshalExact(t *testing.T) {
  function TestOverrides (line 510) | func TestOverrides(t *testing.T) {
  function TestDefaultPost (line 516) | func TestDefaultPost(t *testing.T) {
  function TestAliases (line 523) | func TestAliases(t *testing.T) {
  function TestAliasInConfigFile (line 532) | func TestAliasInConfigFile(t *testing.T) {
  function TestYML (line 547) | func TestYML(t *testing.T) {
  function TestJSON (line 557) | func TestJSON(t *testing.T) {
  function TestTOML (line 567) | func TestTOML(t *testing.T) {
  function TestDotEnv (line 577) | func TestDotEnv(t *testing.T) {
  function TestRemotePrecedence (line 586) | func TestRemotePrecedence(t *testing.T) {
  function TestEnv (line 606) | func TestEnv(t *testing.T) {
  function TestMultipleEnv (line 629) | func TestMultipleEnv(t *testing.T) {
  function TestEmptyEnv (line 642) | func TestEmptyEnv(t *testing.T) {
  function TestEmptyEnv_Allowed (line 657) | func TestEmptyEnv_Allowed(t *testing.T) {
  function TestEnvPrefix (line 674) | func TestEnvPrefix(t *testing.T) {
  function TestAutoEnv (line 697) | func TestAutoEnv(t *testing.T) {
  function TestAutoEnvWithPrefix (line 707) | func TestAutoEnvWithPrefix(t *testing.T) {
  function TestSetEnvKeyReplacer (line 715) | func TestSetEnvKeyReplacer(t *testing.T) {
  function TestEnvKeyReplacer (line 727) | func TestEnvKeyReplacer(t *testing.T) {
  function TestEnvSubConfig (line 734) | func TestEnvSubConfig(t *testing.T) {
  function TestAllKeys (line 753) | func TestAllKeys(t *testing.T) {
  function TestAllKeysWithEnv (line 824) | func TestAllKeysWithEnv(t *testing.T) {
  function TestAliasesOfAliases (line 838) | func TestAliasesOfAliases(t *testing.T) {
  function TestRecursiveAliases (line 846) | func TestRecursiveAliases(t *testing.T) {
  function TestUnmarshal (line 854) | func TestUnmarshal(t *testing.T) {
  function TestUnmarshalWithDefaultDecodeHook (line 897) | func TestUnmarshalWithDefaultDecodeHook(t *testing.T) {
  function TestUnmarshalWithDecoderOptions (line 932) | func TestUnmarshalWithDecoderOptions(t *testing.T) {
  function TestUnmarshalWithAutomaticEnv (line 967) | func TestUnmarshalWithAutomaticEnv(t *testing.T) {
  function TestBindPFlags (line 1092) | func TestBindPFlags(t *testing.T) {
  function TestBindPFlagsStringSlice (line 1125) | func TestBindPFlagsStringSlice(t *testing.T) {
  function TestBindPFlagsStringArray (line 1169) | func TestBindPFlagsStringArray(t *testing.T) {
  function TestBindPFlagsSlices (line 1213) | func TestBindPFlagsSlices(t *testing.T) {
  function TestSliceFlagsReturnCorrectType (line 1236) | func TestSliceFlagsReturnCorrectType(t *testing.T) {
  function TestBindPFlagsIntSlice (line 1252) | func TestBindPFlagsIntSlice(t *testing.T) {
  function TestBindPFlag (line 1295) | func TestBindPFlag(t *testing.T) {
  function TestBindPFlagDetectNilFlag (line 1316) | func TestBindPFlagDetectNilFlag(t *testing.T) {
  function TestBindPFlagStringToString (line 1322) | func TestBindPFlagStringToString(t *testing.T) {
  function TestBindPFlagStringToInt (line 1366) | func TestBindPFlagStringToInt(t *testing.T) {
  function TestBoundCaseSensitivity (line 1410) | func TestBoundCaseSensitivity(t *testing.T) {
  function TestSizeInBytes (line 1434) | func TestSizeInBytes(t *testing.T) {
  function TestFindsNestedKeys (line 1451) | func TestFindsNestedKeys(t *testing.T) {
  function TestReadConfig (line 1543) | func TestReadConfig(t *testing.T) {
  function TestReadConfigWithSetConfigFile (line 1568) | func TestReadConfigWithSetConfigFile(t *testing.T) {
  function TestWrongFileNotFound (line 1576) | func TestWrongFileNotFound(t *testing.T) {
  function TestIsSet (line 1598) | func TestIsSet(t *testing.T) {
  function TestDirsSearch (line 1645) | func TestDirsSearch(t *testing.T) {
  function TestWrongDirsSearchNotFound (line 1666) | func TestWrongDirsSearchNotFound(t *testing.T) {
  function TestWrongDirsSearchNotFoundForMerge (line 1690) | func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
  function TestUnwrapParseErrors (line 1719) | func TestUnwrapParseErrors(t *testing.T) {
  function TestSub (line 1725) | func TestSub(t *testing.T) {
  function TestSubWithKeyDelimiter (line 1749) | func TestSubWithKeyDelimiter(t *testing.T) {
  function TestWriteConfig (line 1799) | func TestWriteConfig(t *testing.T) {
  function TestWriteConfigTOML (line 1877) | func TestWriteConfigTOML(t *testing.T) {
  function TestWriteConfigDotEnv (line 1928) | func TestWriteConfigDotEnv(t *testing.T) {
  function TestSafeWriteConfig (line 1977) | func TestSafeWriteConfig(t *testing.T) {
  function TestSafeWriteConfigWithMissingConfigPath (line 1991) | func TestSafeWriteConfigWithMissingConfigPath(t *testing.T) {
  function TestSafeWriteConfigWithExistingFile (line 2000) | func TestSafeWriteConfigWithExistingFile(t *testing.T) {
  function TestSafeWriteAsConfig (line 2014) | func TestSafeWriteAsConfig(t *testing.T) {
  function TestSafeWriteConfigAsWithExistingFile (line 2026) | func TestSafeWriteConfigAsWithExistingFile(t *testing.T) {
  function TestWriteHiddenFile (line 2037) | func TestWriteHiddenFile(t *testing.T) {
  function TestMergeConfig (line 2098) | func TestMergeConfig(t *testing.T) {
  function TestMergeConfigWithSetConfigFile (line 2127) | func TestMergeConfigWithSetConfigFile(t *testing.T) {
  function TestMergeConfigOverrideType (line 2135) | func TestMergeConfigOverrideType(t *testing.T) {
  function TestMergeConfigNoMerge (line 2148) | func TestMergeConfigNoMerge(t *testing.T) {
  function TestMergeConfigMap (line 2168) | func TestMergeConfigMap(t *testing.T) {
  function TestUnmarshalingWithAliases (line 2200) | func TestUnmarshalingWithAliases(t *testing.T) {
  function TestSetConfigNameClearsFileCache (line 2223) | func TestSetConfigNameClearsFileCache(t *testing.T) {
  function TestShadowedNestedValue (line 2232) | func TestShadowedNestedValue(t *testing.T) {
  function TestDotParameter (line 2258) | func TestDotParameter(t *testing.T) {
  function TestCaseInsensitive (line 2275) | func TestCaseInsensitive(t *testing.T) {
  function TestCaseInsensitiveSet (line 2320) | func TestCaseInsensitiveSet(t *testing.T) {
  function TestParseNested (line 2359) | func TestParseNested(t *testing.T) {
  function doTestCaseInsensitive (line 2387) | func doTestCaseInsensitive(t *testing.T, typ, config string) {
  function newViperWithConfigFile (line 2401) | func newViperWithConfigFile(t *testing.T) (*Viper, string) {
  function newViperWithSymlinkedConfigFile (line 2414) | func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, stri...
  function TestWatchFile (line 2438) | func TestWatchFile(t *testing.T) {
  function TestUnmarshal_DotSeparatorBackwardCompatibility (line 2498) | func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
  function TestKeyDelimiter (line 2535) | func TestKeyDelimiter(t *testing.T) {
  function TestSliceIndexAccess (line 2609) | func TestSliceIndexAccess(t *testing.T) {
  function TestIsPathShadowedInFlatMap (line 2633) | func TestIsPathShadowedInFlatMap(t *testing.T) {
  function TestFlagShadow (line 2659) | func TestFlagShadow(t *testing.T) {
  function BenchmarkGetBool (line 2678) | func BenchmarkGetBool(b *testing.B) {
  function BenchmarkGet (line 2690) | func BenchmarkGet(b *testing.B) {
  function BenchmarkGetBoolFromMap (line 2703) | func BenchmarkGetBoolFromMap(b *testing.B) {
  function skipWindows (line 2716) | func skipWindows(t *testing.T) {
Condensed preview — 63 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (303K chars).
[
  {
    "path": ".editorconfig",
    "chars": 271,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 4\nindent_style = space\ninsert_final_newline = true\ntrim_"
  },
  {
    "path": ".envrc",
    "chars": 231,
    "preview": "if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then\n  source_url \"https://raw.githubusercontent.com/nix-comm"
  },
  {
    "path": ".github/.editorconfig",
    "chars": 33,
    "preview": "[{*.yml,*.yaml}]\nindent_size = 2\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yaml",
    "chars": 3471,
    "preview": "name: 🐛 Bug report\ndescription: Report a bug to help us improve Viper\nlabels: [kind/bug]\nbody:\n- type: markdown\n  attrib"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 454,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: ❓ Ask a question\n    url: https://github.com/spf13/viper/discussion"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yaml",
    "chars": 1490,
    "preview": "name: 🎉 Feature request\ndescription: Suggest an idea for Viper\nlabels: [kind/enhancement]\nbody:\n- type: markdown\n  attri"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATES.md",
    "chars": 711,
    "preview": "<!--\nThank you for sending a pull request! Here some tips for contributors:\n\n1. Fill the description template below.\n2. "
  },
  {
    "path": ".github/dependabot.yaml",
    "chars": 271,
    "preview": "version: 2\n\nupdates:\n  - package-ecosystem: gomod\n    directory: /\n    labels:\n      - area/dependencies\n    schedule:\n "
  },
  {
    "path": ".github/octoslash/policies/collaborator.cedar",
    "chars": 66,
    "preview": "permit(\n\tprincipal in Role::\"Collaborator\",\n\taction,\n\tresource\n);\n"
  },
  {
    "path": ".github/octoslash/policies/triager.cedar",
    "chars": 175,
    "preview": "permit(\n\tprincipal in Role::\"Triager\",\n\taction in [Action::\"close\", Action::\"add-label\", Action::\"remove-label\", Action:"
  },
  {
    "path": ".github/octoslash/principals.json",
    "chars": 341,
    "preview": "[\n    {\n        \"uid\": { \"type\": \"User\", \"id\": \"1226384\" },\n        \"attrs\": { \"login\": \"sagikazarmark\" },\n        \"pare"
  },
  {
    "path": ".github/release.yml",
    "chars": 713,
    "preview": "changelog:\n  exclude:\n    labels:\n      - release-note/ignore\n  categories:\n    - title: Exciting New Features 🎉\n      l"
  },
  {
    "path": ".github/workflows/checks.yaml",
    "chars": 823,
    "preview": "name: PR Checks\n\non:\n  pull_request:\n    types: [opened, labeled, unlabeled, synchronize]\n\npermissions:\n  pull-requests:"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "chars": 3076,
    "preview": "name: CI\n\non:\n  push:\n    branches: [master]\n  pull_request:\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    name: Bu"
  },
  {
    "path": ".github/workflows/octoslash.yaml",
    "chars": 342,
    "preview": "name: Octoslash\n\non: issue_comment\n\npermissions:\n  issues: write\n  pull-requests: write\n\njobs:\n  run:\n    name: Run\n    "
  },
  {
    "path": ".github/workflows/stale.yaml",
    "chars": 897,
    "preview": "name: \"Close stale issues\"\n\non:\n  schedule:\n    - cron: \"0 3 * * *\"\n\npermissions:\n  actions: write\n  issues: write\n  # c"
  },
  {
    "path": ".gitignore",
    "chars": 82,
    "preview": "/.devenv/\n/.direnv/\n/.idea/\n/.pre-commit-config.yaml\n/bin/\n/build/\n/var/\n/vendor/\n"
  },
  {
    "path": ".golangci.yaml",
    "chars": 2990,
    "preview": "version: \"2\"\n\nrun:\n  timeout: 5m\n\nlinters:\n  enable:\n    - bodyclose\n    - dogsled\n    - dupl\n    - durationcheck\n    - "
  },
  {
    "path": ".yamlignore",
    "chars": 24,
    "preview": "# TODO: FIXME\n/.github/\n"
  },
  {
    "path": ".yamllint.yaml",
    "chars": 93,
    "preview": "ignore-from-file: [.gitignore, .yamlignore]\n\nextends: default\n\nrules:\n  line-length: disable\n"
  },
  {
    "path": "LICENSE",
    "chars": 1079,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Steve Francia\n\nPermission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "Makefile",
    "chars": 2430,
    "preview": "# A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html\n\nOS = $(shell uname | t"
  },
  {
    "path": "README.md",
    "chars": 21376,
    "preview": "> ## Viper v2 Feedback\n>\n> Viper is heading towards v2 and we would love to hear what _**you**_ would\n> like to see in i"
  },
  {
    "path": "TROUBLESHOOTING.md",
    "chars": 1756,
    "preview": "# Troubleshooting\n\n## Unmarshaling doesn't work\n\nThe most common reason for this issue is improper use of struct tags (e"
  },
  {
    "path": "UPGRADE.md",
    "chars": 4622,
    "preview": "# Update Log\n\n**This document details any major updates required to use new features or improvements in Viper.**\n\n## v1."
  },
  {
    "path": "encoding.go",
    "chars": 4013,
    "preview": "package viper\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/spf13/viper/internal/encoding/dotenv\"\n\t\"github.com/sp"
  },
  {
    "path": "encoding_test.go",
    "chars": 1937,
    "preview": "package viper\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype"
  },
  {
    "path": "errors.go",
    "chars": 2591,
    "preview": "package viper\n\nimport (\n\t\"fmt\"\n)\n\n// FileLookupError is returned when Viper cannot resolve a configuration file.\n//\n// T"
  },
  {
    "path": "experimental.go",
    "chars": 204,
    "preview": "package viper\n\n// ExperimentalBindStruct tells Viper to use the new bind struct feature.\nfunc ExperimentalBindStruct() O"
  },
  {
    "path": "file.go",
    "chars": 2619,
    "preview": "package viper\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/sagikazarmark/locafero\"\n\t\"github.com/spf13/afero\"\n)\n\n// Exp"
  },
  {
    "path": "finder.go",
    "chars": 1002,
    "preview": "package viper\n\nimport (\n\t\"errors\"\n\n\t\"github.com/spf13/afero\"\n)\n\n// WithFinder sets a custom [Finder].\nfunc WithFinder(f "
  },
  {
    "path": "finder_example_test.go",
    "chars": 1614,
    "preview": "package viper_test\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/sagikazarmark/locafero\"\n\t\"github.com/spf13/afero\"\n\n\t\"github.com/spf13/"
  },
  {
    "path": "finder_test.go",
    "chars": 679,
    "preview": "package viper\n\nimport (\n\t\"testing\"\n\n\t\"github.com/spf13/afero\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretch"
  },
  {
    "path": "flags.go",
    "chars": 1345,
    "preview": "package viper\n\nimport \"github.com/spf13/pflag\"\n\n// FlagValueSet is an interface that users can implement\n// to bind a se"
  },
  {
    "path": "flags_test.go",
    "chars": 1362,
    "preview": "package viper\n\nimport (\n\t\"testing\"\n\n\t\"github.com/spf13/pflag\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretch"
  },
  {
    "path": "flake.nix",
    "chars": 1337,
    "preview": "{\n  description = \"Viper\";\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixpkgs-unstable\";\n    flake-parts.url "
  },
  {
    "path": "go.mod",
    "chars": 747,
    "preview": "module github.com/spf13/viper\n\ngo 1.23.0\n\nrequire (\n\tgithub.com/fsnotify/fsnotify v1.9.0\n\tgithub.com/go-viper/mapstructu"
  },
  {
    "path": "go.sum",
    "chars": 3691,
    "preview": "github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.m"
  },
  {
    "path": "internal/encoding/dotenv/codec.go",
    "chars": 1194,
    "preview": "package dotenv\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/subosito/gotenv\"\n)\n\nconst keyDelimiter = \"_\"\n\n"
  },
  {
    "path": "internal/encoding/dotenv/codec_test.go",
    "chars": 934,
    "preview": "package dotenv\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// "
  },
  {
    "path": "internal/encoding/dotenv/map_utils.go",
    "chars": 966,
    "preview": "package dotenv\n\nimport (\n\t\"strings\"\n\n\t\"github.com/spf13/cast\"\n)\n\n// flattenAndMergeMap recursively flattens the given ma"
  },
  {
    "path": "internal/encoding/json/codec.go",
    "chars": 521,
    "preview": "package json\n\nimport (\n\t\"encoding/json\"\n)\n\n// Codec implements the encoding.Encoder and encoding.Decoder interfaces for "
  },
  {
    "path": "internal/encoding/json/codec_test.go",
    "chars": 1338,
    "preview": "package json\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// en"
  },
  {
    "path": "internal/encoding/toml/codec.go",
    "chars": 463,
    "preview": "package toml\n\nimport (\n\t\"github.com/pelletier/go-toml/v2\"\n)\n\n// Codec implements the encoding.Encoder and encoding.Decod"
  },
  {
    "path": "internal/encoding/toml/codec_test.go",
    "chars": 1482,
    "preview": "package toml\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// or"
  },
  {
    "path": "internal/encoding/yaml/codec.go",
    "chars": 445,
    "preview": "package yaml\n\nimport \"go.yaml.in/yaml/v3\"\n\n// Codec implements the encoding.Encoder and encoding.Decoder interfaces for "
  },
  {
    "path": "internal/encoding/yaml/codec_test.go",
    "chars": 2015,
    "preview": "package yaml\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// or"
  },
  {
    "path": "internal/features/bind_struct.go",
    "chars": 158,
    "preview": "//go:build viper_bind_struct\n\npackage features\n\n// BindStruct is a feature flag for enabling/disabling the config bindin"
  },
  {
    "path": "internal/features/bind_struct_default.go",
    "chars": 160,
    "preview": "//go:build !viper_bind_struct\n\npackage features\n\n// BindStruct is a feature flag for enabling/disabling the config bindi"
  },
  {
    "path": "internal/features/finder.go",
    "chars": 133,
    "preview": "//go:build viper_finder\n\npackage features\n\n// Finder is a feature flag for enabling/disabling the config finder.\nconst F"
  },
  {
    "path": "internal/features/finder_default.go",
    "chars": 135,
    "preview": "//go:build !viper_finder\n\npackage features\n\n// Finder is a feature flag for enabling/disabling the config finder.\nconst "
  },
  {
    "path": "internal/testutil/filepath.go",
    "chars": 245,
    "preview": "package testutil\n\nimport (\n\t\"path/filepath\"\n\t\"testing\"\n)\n\n// AbsFilePath calls filepath.Abs on path.\nfunc AbsFilePath(t "
  },
  {
    "path": "logger.go",
    "chars": 544,
    "preview": "package viper\n\nimport (\n\t\"context\"\n\t\"log/slog\"\n)\n\n// WithLogger sets a custom logger.\nfunc WithLogger(l *slog.Logger) Op"
  },
  {
    "path": "overrides_test.go",
    "chars": 6162,
    "preview": "package viper\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/spf13/cast\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype la"
  },
  {
    "path": "remote/go.mod",
    "chars": 4119,
    "preview": "module github.com/spf13/viper/remote\n\ngo 1.23.8\n\nreplace github.com/spf13/viper => ../\n\nrequire (\n\tgithub.com/sagikazarm"
  },
  {
    "path": "remote/go.sum",
    "chars": 35743,
    "preview": "cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=\ncloud.google.com/go v0.120.0/go.mod h1:/beW"
  },
  {
    "path": "remote/remote.go",
    "chars": 2965,
    "preview": "// Copyright © 2015 Steve Francia <spf@spf13.com>.\n//\n// Use of this source code is governed by an MIT-style\n// license "
  },
  {
    "path": "remote.go",
    "chars": 9328,
    "preview": "package viper\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"slices\"\n)\n\n// SupportedRemoteProviders are universally suppor"
  },
  {
    "path": "util.go",
    "chars": 5003,
    "preview": "// Copyright © 2014 Steve Francia <spf@spf13.com>.\n//\n// Use of this source code is governed by an MIT-style\n// license "
  },
  {
    "path": "util_test.go",
    "chars": 1943,
    "preview": "// Copyright © 2016 Steve Francia <spf@spf13.com>.\n//\n// Use of this source code is governed by an MIT-style\n// license "
  },
  {
    "path": "viper.go",
    "chars": 64403,
    "preview": "// Copyright © 2014 Steve Francia <spf@spf13.com>.\n//\n// Use of this source code is governed by an MIT-style\n// license "
  },
  {
    "path": "viper_test.go",
    "chars": 66541,
    "preview": "// Copyright © 2014 Steve Francia <spf@spf13.com>.\n//\n// Use of this source code is governed by an MIT-style\n// license "
  },
  {
    "path": "viper_yaml_test.go",
    "chars": 781,
    "preview": "package viper\n\nvar yamlExample = []byte(`Hacker: true\nname: steve\nhobbies:\n    - skateboarding\n    - snowboarding\n    - "
  }
]

About this extraction

This page contains the full source code of the spf13/viper GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 63 files (272.1 KB), approximately 89.5k tokens, and a symbol index with 436 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!