[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 4\nindent_style = space\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.go]\nindent_style = tab\n\n[{Makefile,*.mk}]\nindent_style = tab\n\n[*.nix]\nindent_size = 2\n\n[.golangci.yaml]\nindent_size = 2\n"
  },
  {
    "path": ".envrc",
    "content": "if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then\n  source_url \"https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc\" \"sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4=\"\nfi\nuse flake . --impure\n"
  },
  {
    "path": ".github/.editorconfig",
    "content": "[{*.yml,*.yaml}]\nindent_size = 2\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yaml",
    "content": "name: 🐛 Bug report\ndescription: Report a bug to help us improve Viper\nlabels: [kind/bug]\nbody:\n- type: markdown\n  attributes:\n    value: |\n      Thank you for submitting a bug report!\n\n      Please fill out the template below to make it easier to debug your problem.\n\n      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).\n- type: checkboxes\n  attributes:\n    label: Preflight Checklist\n    description: Please ensure you've completed all of the following.\n    options:\n      - 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.\n        required: true\n      - label: I am not looking for support or already pursued the available [support channels](https://github.com/spf13/viper/issues/new/choose) without success.\n        required: true\n      - label: I have checked the [troubleshooting guide](https://github.com/spf13/viper/blob/master/TROUBLESHOOTING.md) for my problem, without success.\n        required: true\n- type: input\n  attributes:\n    label: Viper Version\n    description: What version of Viper are you using?\n    placeholder: 1.8.1\n  validations:\n    required: true\n- type: input\n  attributes:\n    label: Go Version\n    description: What version of Go are you using?\n    placeholder: \"1.16\"\n  validations:\n    required: true\n- type: dropdown\n  attributes:\n    label: Config Source\n    description: What sources do you load configuration from?\n    options:\n      - Manual set\n      - Flags\n      - Environment variables\n      - Files\n      - Remove K/V stores\n      - Defaults\n    multiple: true\n  validations:\n    required: true\n- type: dropdown\n  attributes:\n    label: Format\n    description: Which file formats do you use?\n    options:\n      - JSON\n      - YAML\n      - TOML\n      - Dotenv\n      - HCL\n      - Java properties\n      - INI\n      - Other (specify below)\n    multiple: true\n- type: input\n  attributes:\n    label: Repl.it link\n    description: Complete example on Repl.it reproducing the issue. [Here](https://repl.it/@sagikazarmark/Viper-example) is an example you can use.\n    placeholder: https://repl.it/@sagikazarmark/Viper-example\n- type: textarea\n  attributes:\n    label: Code reproducing the issue\n    description: Please provide a Repl.it link if possible.\n    render: go\n    placeholder: |\n      package main\n\n      import (\n        \"github.com/spf13/viper\"\n      )\n\n      func main() {\n        v := viper.New()\n\n        // ...\n\n        var config Config\n\n        err = v.Unmarshal(&config)\n        if err != nil {\n          panic(err)\n        }\n      }\n- type: textarea\n  attributes:\n    label: Expected Behavior\n    description: A clear and concise description of what you expected to happen.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Actual Behavior\n    description: A clear description of what actually happens.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Steps To Reproduce\n    description: Steps to reproduce the behavior if it is not self-explanatory.\n    placeholder: |\n      1. In this environment...\n      2. With this config...\n      3. Run '...'\n      4. See error...\n- type: textarea\n  attributes:\n    label: Additional Information\n    description: Links? References? Anything that will give us more context about the issue that you are encountering!\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: ❓ Ask a question\n    url: https://github.com/spf13/viper/discussions/new?category=q-a\n    about:  Ask and discuss questions with other Viper community members\n\n  - name: 📓 Reference\n    url: https://pkg.go.dev/mod/github.com/spf13/viper\n    about: Check the Go code reference\n\n  - name: 💬 Slack channel\n    url: https://gophers.slack.com/messages/viper\n    about: Please ask and answer questions here\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yaml",
    "content": "name: 🎉 Feature request\ndescription: Suggest an idea for Viper\nlabels: [kind/enhancement]\nbody:\n- type: markdown\n  attributes:\n    value: |\n      Thank you for submitting a feature request!\n\n      Please describe what you would like to change/add and why in detail by filling out the template below.\n\n      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).\n- type: checkboxes\n  attributes:\n    label: Preflight Checklist\n    description: Please ensure you've completed all of the following.\n    options:\n      - 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.\n        required: true\n- type: textarea\n  attributes:\n    label: Problem Description\n    description: A clear and concise description of the problem you are seeking to solve with this feature request.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Proposed Solution\n    description: A clear and concise description of what would you like to happen.\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Alternatives Considered\n    description: A clear and concise description of any alternative solutions or features you've considered.\n- type: textarea\n  attributes:\n    label: Additional Information\n    description: Add any other context about the problem here.\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATES.md",
    "content": "<!--\nThank you for sending a pull request! Here some tips for contributors:\n\n1. Fill the description template below.\n2. Include appropriate tests (if necessary). Make sure that all CI checks passed.\n3. If the Pull Request is a work in progress, make use of GitHub's \"Draft PR\" feature and mark it as such.\n-->\n\n**Overview**:\n<!-- Describe your changes briefly here. -->\n\n**What problem does it solve?**:\n<!--\n- Please state in detail why we need this PR and what it solves.\n- If your PR closes some of the existing issues, please add links to them here.\n  Mentioned issues will be automatically closed.\n  Usage: \"Closes #<issue number>\", or \"Closes (paste link of issue)\"\n-->\n\n**Special notes for a reviewer**:\n"
  },
  {
    "path": ".github/dependabot.yaml",
    "content": "version: 2\n\nupdates:\n  - package-ecosystem: gomod\n    directory: /\n    labels:\n      - area/dependencies\n    schedule:\n      interval: daily\n\n  - package-ecosystem: github-actions\n    directory: /\n    labels:\n      - area/dependencies\n    schedule:\n      interval: daily\n"
  },
  {
    "path": ".github/octoslash/policies/collaborator.cedar",
    "content": "permit(\n\tprincipal in Role::\"Collaborator\",\n\taction,\n\tresource\n);\n"
  },
  {
    "path": ".github/octoslash/policies/triager.cedar",
    "content": "permit(\n\tprincipal in Role::\"Triager\",\n\taction in [Action::\"close\", Action::\"add-label\", Action::\"remove-label\", Action::\"self-assign\", Action::\"self-unassign\"],\n\tresource\n);\n"
  },
  {
    "path": ".github/octoslash/principals.json",
    "content": "[\n    {\n        \"uid\": { \"type\": \"User\", \"id\": \"1226384\" },\n        \"attrs\": { \"login\": \"sagikazarmark\" },\n        \"parents\": [{ \"type\": \"Role\", \"id\": \"Collaborator\" }]\n    },\n    {\n        \"uid\": { \"type\": \"User\", \"id\": \"805695\" },\n        \"attrs\": { \"login\": \"spacez320\" },\n        \"parents\": [{ \"type\": \"Role\", \"id\": \"Triager\" }]\n    }\n]\n"
  },
  {
    "path": ".github/release.yml",
    "content": "changelog:\n  exclude:\n    labels:\n      - release-note/ignore\n  categories:\n    - title: Exciting New Features 🎉\n      labels:\n        - kind/feature\n        - release-note/new-feature\n    - title: Enhancements 🚀\n      labels:\n        - kind/enhancement\n        - release-note/enhancement\n    - title: Bug Fixes 🐛\n      labels:\n        - kind/bug\n        - release-note/bug-fix\n    - title: Breaking Changes 🛠\n      labels:\n        - release-note/breaking-change\n    - title: Deprecations ❌\n      labels:\n        - release-note/deprecation\n    - title: Dependency Updates ⬆️\n      labels:\n        - area/dependencies\n        - release-note/dependency-update\n    - title: Other Changes\n      labels:\n        - \"*\"\n"
  },
  {
    "path": ".github/workflows/checks.yaml",
    "content": "name: PR Checks\n\non:\n  pull_request:\n    types: [opened, labeled, unlabeled, synchronize]\n\npermissions:\n  pull-requests: read\n\njobs:\n  release-label:\n    name: Release note label\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Check minimum labels\n        uses: mheap/github-action-required-labels@388fd6af37b34cdfe5a23b37060e763217e58b03 # v5.5\n        with:\n          mode: minimum\n          count: 1\n          labels: |\n            release-note/ignore\n            kind/feature\n            release-note/new-feature\n            kind/enhancement\n            release-note/enhancement\n            kind/bug\n            release-note/bug-fix\n            release-note/breaking-change\n            release-note/deprecation\n            area/dependencies\n            release-note/dependency-update\n            release-note/misc\n"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "content": "name: CI\n\non:\n  push:\n    branches: [master]\n  pull_request:\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - goos: js\n            goarch: wasm\n          - goos: aix\n            goarch: ppc64\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n      - name: Set up Go\n        uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version: \"1.25\"\n\n      - name: Build\n        run: go build .\n        env:\n          GOOS: ${{ matrix.goos }}\n          GOARCH: ${{ matrix.goarch }}\n\n  test:\n    name: Test\n    runs-on: ${{ matrix.os }}\n\n    strategy:\n      # Fail fast is disabled because there are Go version specific features and tests\n      # that should be able to fail independently.\n      fail-fast: false\n      matrix:\n        os: [ubuntu-latest, macos-latest, windows-latest]\n        go: [\"1.23\", \"1.24\", \"1.25\"]\n        tags: [\"\", \"viper_finder\", \"viper_bind_struct\"]\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n      - name: Set up Go\n        uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version: ${{ matrix.go }}\n\n      - name: Test\n        run: go test -race -v -tags '${{ matrix.tags }}' -shuffle=on ./...\n        if: runner.os != 'Windows'\n\n      - name: Test (without race detector)\n        run: go test -v -tags '${{ matrix.tags }}' -shuffle=on ./...\n        if: runner.os == 'Windows'\n\n  lint:\n    name: Lint\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n      - name: Set up Go\n        uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version: \"1.25\"\n\n      - name: Lint\n        uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0\n        with:\n          version: v2.4.0\n\n  dev:\n    name: Developer environment\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n      - name: Set up Nix\n        uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # v27\n        with:\n          extra_nix_config: |\n            access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}\n\n      - name: Check\n        run: nix flake check --impure\n\n      - name: Dev shell\n        run: nix develop --impure\n\n  dependency-review:\n    name: Dependency review\n    runs-on: ubuntu-latest\n    if: github.event_name == 'pull_request'\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n      - name: Dependency Review\n        uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0\n"
  },
  {
    "path": ".github/workflows/octoslash.yaml",
    "content": "name: Octoslash\n\non: issue_comment\n\npermissions:\n  issues: write\n  pull-requests: write\n\njobs:\n  run:\n    name: Run\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n      - name: Octoslash\n        uses: sagikazarmark/octoslash-action@v0\n"
  },
  {
    "path": ".github/workflows/stale.yaml",
    "content": "name: \"Close stale issues\"\n\non:\n  schedule:\n    - cron: \"0 3 * * *\"\n\npermissions:\n  actions: write\n  issues: write\n  # contents: write # only for delete-branch option\n  # pull-requests: write\n\njobs:\n  stale:\n    name: Stale\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/stale@v9\n        with:\n          days-before-stale: 30\n          days-before-close: 5\n          days-before-pr-stale: -1 # Ignore all PRs\n          stale-issue-message: \"Issues with no activity for 30 days are marked stale and subject to being closed.\"\n          close-issue-message: \"This issue has been closed for inactivity. Please re-open if this was a mistake.\"\n          exempt-issue-labels: \"lifecycle/keep\"\n          stale-issue-label: \"lifecycle/stale\"\n          operations-per-run: 500 # Must respect GitHub API limits\n          debug-only: false # Set this to 'true' to disable stale issue management\n"
  },
  {
    "path": ".gitignore",
    "content": "/.devenv/\n/.direnv/\n/.idea/\n/.pre-commit-config.yaml\n/bin/\n/build/\n/var/\n/vendor/\n"
  },
  {
    "path": ".golangci.yaml",
    "content": "version: \"2\"\n\nrun:\n  timeout: 5m\n\nlinters:\n  enable:\n    - bodyclose\n    - dogsled\n    - dupl\n    - durationcheck\n    - exhaustive\n    - gocritic\n    - godot\n    - gomoddirectives\n    - goprintffuncname\n    - govet\n    - importas\n    - ineffassign\n    - makezero\n    - misspell\n    - nakedret\n    - nilerr\n    - noctx\n    - nolintlint\n    - prealloc\n    - predeclared\n    - revive\n    - rowserrcheck\n    - sqlclosecheck\n    - staticcheck\n    - tparallel\n    - unconvert\n    - unparam\n    - unused\n    - wastedassign\n    - whitespace\n\n    # fixme\n    # - cyclop\n    # - errcheck\n    # - errorlint\n    # - exhaustivestruct\n    # - forbidigo\n    # - forcetypeassert\n    # - gochecknoglobals\n    # - gochecknoinits\n    # - gocognit\n    # - goconst\n    # - gocyclo\n    # - gosec\n    # - gosimple\n    # - ifshort\n    # - lll\n    # - nlreturn\n    # - paralleltest\n    # - scopelint\n    # - thelper\n    # - wrapcheck\n\n    # unused\n    # - depguard\n    # - goheader\n    # - gomodguard\n\n    # don't enable:\n    # - asciicheck\n    # - funlen\n    # - godox\n    # - goerr113\n    # - gomnd\n    # - interfacer\n    # - maligned\n    # - nestif\n    # - testpackage\n    # - wsl\n\n  exclusions:\n    rules:\n      - linters:\n        - errcheck\n        - noctx\n        path: _test.go\n    presets:\n      - std-error-handling\n\n  settings:\n    revive:\n      rules:\n        - name: blank-imports\n        - name: context-as-argument\n          arguments:\n            - allowTypesBefore: \"*testing.T\"\n        - name: context-keys-type\n        - name: dot-imports\n        - name: empty-block\n        - name: error-naming\n        - name: error-return\n        - name: error-strings\n        - name: errorf\n        - name: exported\n          arguments:\n            - \"sayRepetitiveInsteadOfStutters\"  # make error messages clearer\n        - name: increment-decrement\n        - name: indent-error-flow\n        - name: package-comments\n          disabled: true # disable package comments rule for now\n        - name: range\n        - name: redefines-builtin-id\n        - name: superfluous-else\n        - name: time-naming\n        - name: unexported-return\n        - name: unreachable-code\n        - name: unused-parameter\n        - name: var-declaration\n        - name: var-naming\n\n        # TODO: add more revive rules\n\n    misspell:\n      locale: US\n    nolintlint:\n      allow-unused: false # report any unused nolint directives\n      require-specific: false # don't require nolint directives to be specific about which linter is being skipped\n    gocritic:\n      # Enable multiple checks by tags. See \"Tags\" section in https://github.com/go-critic/go-critic#usage.\n      enabled-tags:\n        - diagnostic\n        - experimental\n        - opinionated\n        - style\n      disabled-checks:\n        - importShadow\n        - unnamedResult\n\nformatters:\n  enable:\n    - gci\n    - gofmt\n    - gofumpt\n    - goimports\n    # - golines\n\n  settings:\n    gci:\n      sections:\n        - standard\n        - default\n        - localmodule\n"
  },
  {
    "path": ".yamlignore",
    "content": "# TODO: FIXME\n/.github/\n"
  },
  {
    "path": ".yamllint.yaml",
    "content": "ignore-from-file: [.gitignore, .yamlignore]\n\nextends: default\n\nrules:\n  line-length: disable\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Steve Francia\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "Makefile",
    "content": "# A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html\n\nOS = $(shell uname | tr A-Z a-z)\nexport PATH := $(abspath bin/):${PATH}\n\n# Build variables\nBUILD_DIR ?= build\nexport CGO_ENABLED ?= 0\nexport GOOS = $(shell go env GOOS)\nifeq (${VERBOSE}, 1)\nifeq ($(filter -v,${GOARGS}),)\n\tGOARGS += -v\nendif\nTEST_FORMAT = short-verbose\nendif\n\n# Dependency versions\nGOTESTSUM_VERSION = 1.9.0\nGOLANGCI_VERSION = 1.53.3\n\n# Add the ability to override some variables\n# Use with care\n-include override.mk\n\n.PHONY: clear\nclear: ## Clear the working area and the project\n\trm -rf bin/\n\n.PHONY: check\ncheck: test lint ## Run tests and linters\n\n\nTEST_PKGS ?= ./...\n.PHONY: test\ntest: TEST_FORMAT ?= short\ntest: SHELL = /bin/bash\ntest: export CGO_ENABLED=1\ntest: bin/gotestsum ## Run tests\n\t@mkdir -p ${BUILD_DIR}\n\tbin/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},./...)\n\n.PHONY: lint\nlint: lint-go lint-yaml\nlint: ## Run linters\n\n.PHONY: lint-go\nlint-go:\n\tgolangci-lint run $(if ${CI},--out-format github-actions,)\n\n.PHONY: lint-yaml\nlint-yaml:\n\tyamllint $(if ${CI},-f github,) --no-warnings .\n\n.PHONY: fmt\nfmt: ## Format code\n\tgolangci-lint run --fix\n\ndeps: bin/golangci-lint bin/gotestsum yamllint\ndeps: ## Install dependencies\n\nbin/gotestsum:\n\t@mkdir -p bin\n\tcurl -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\n\nbin/golangci-lint:\n\t@mkdir -p bin\n\tcurl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- v${GOLANGCI_VERSION}\n\n.PHONY: yamllint\nyamllint:\n\tpip3 install --user yamllint\n\n# Add custom targets here\n-include custom.mk\n\n.PHONY: list\nlist: ## List all make targets\n\t@${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\n\n.PHONY: help\n.DEFAULT_GOAL := help\nhelp:\n\t@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = \":.*?## \"}; {printf \"\\033[36m%-30s\\033[0m %s\\n\", $$1, $$2}'\n\n# Variable outputting/exporting rules\nvar-%: ; @echo $($*)\nvarexport-%: ; @echo $*=$($*)\n"
  },
  {
    "path": "README.md",
    "content": "> ## Viper v2 Feedback\n>\n> Viper is heading towards v2 and we would love to hear what _**you**_ would\n> like to see in it. Share your thoughts here:\n> https://forms.gle/R6faU74qPRPAzchZ9\n>\n> **Thank you!**\n\n![viper logo](https://github.com/user-attachments/assets/acae9193-2974-41f3-808d-2d433f5ada5e)\n\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration)\n[![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go)\n\n[![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)\n[![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)\n[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper)\n![Go Version](https://img.shields.io/badge/go%20version-%3E=1.23-61CFDD.svg?style=flat-square)\n[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper)\n\n**Go configuration with fangs!**\n\nMany Go projects are built using Viper including:\n\n* [Hugo](http://gohugo.io)\n* [EMC RexRay](http://rexray.readthedocs.org/en/stable/)\n* [Imgur’s Incus](https://github.com/Imgur/incus)\n* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)\n* [Docker Notary](https://github.com/docker/Notary)\n* [BloomApi](https://www.bloomapi.com/)\n* [doctl](https://github.com/digitalocean/doctl)\n* [Clairctl](https://github.com/jgsqware/clairctl)\n* [Mercure](https://mercure.rocks)\n* [Meshery](https://github.com/meshery/meshery)\n* [Bearer](https://github.com/bearer/bearer)\n* [Coder](https://github.com/coder/coder)\n* [Vitess](https://vitess.io/)\n\n\n## Install\n\n```shell\ngo get github.com/spf13/viper\n```\n\n> **NOTE** Viper uses [Go Modules](https://go.dev/wiki/Modules) to manage dependencies.\n\n\n## Why use Viper?\n\nViper is a complete configuration solution for Go applications including\n[12-Factor apps](https://12factor.net/#the_twelve_factors). It is designed to\nwork within any application, and can handle all types of configuration needs\nand formats. It supports:\n\n* setting defaults\n* setting explicit values\n* reading config files\n* dynamic discovery of config files across multiple locations\n* reading from environment variables\n* reading from remote systems (e.g. Etcd or Consul)\n* reading from command line flags\n* reading from buffers\n* live watching and updating configuration\n* aliasing configuration keys for easy refactoring\n\nViper can be thought of as a registry for all of your applications'\nconfiguration needs.\n\n\n## Putting Values in Viper\n\nViper can read from multiple configuration sources and merges them together\ninto one set of configuration keys and values.\n\nViper uses the following precedence for merging:\n\n * explicit call to `Set`\n * flags\n * environment variables\n * config files\n * external key/value stores\n * defaults\n\n> **NOTE** Viper configuration keys are case insensitive.\n\n### Reading Config Files\n\nViper requires minimal configuration to load config files. Viper currently supports:\n\n* JSON\n* TOML\n* YAML\n* INI\n* envfile\n* Java Propeties\n\nA single Viper instance only supports a single configuration file, but multiple\npaths may be searched for one.\n\nHere is an example of how to use Viper to search for and read a configuration\nfile. At least one path should be provided where a configuration file is\nexpected.\n\n```go\n// Name of the config file without an extension (Viper will intuit the type\n// from an extension on the actual file)\nviper.SetConfigName(\"config\")\n\n// Add search paths to find the file\nviper.AddConfigPath(\"/etc/appname/\")\nviper.AddConfigPath(\"$HOME/.appname\")\nviper.AddConfigPath(\".\")\n\n// Find and read the config file\nerr := viper.ReadInConfig()\n\n// Handle errors\nif err != nil {\n\tpanic(fmt.Errorf(\"fatal error config file: %w\", err))\n}\n```\n\nYou can handle the specific case where no config file is found.\n\n```go\nvar fileLookupError viper.FileLookupError\nif err := viper.ReadInConfig(); err != nil {\n    if errors.As(err, &fileLookupError) {\n        // Indicates an explicitly set config file is not found (such as with\n        // using `viper.SetConfigFile`) or that no config file was found in\n        // any search path (such as when using `viper.AddConfigPath`)\n    } else {\n        // Config file was found but another error was produced\n    }\n}\n\n// Config file found and successfully parsed\n```\n\n> **NOTE (since 1.6)** You can also have a file without an extension and\n> specify the format programmatically, which is useful for files that naturally\n> have no extension (e.g., `.bashrc`).\n\n### Writing Config Files\n\nAt times you may want to store all configuration modifications made during run\ntime.\n\n```go\n// Writes current config to the path set by `AddConfigPath` and `SetConfigName`\nviper.WriteConfig()\nviper.SafeWriteConfig() // Like the above, but will error if the config file exists\n\n// Writes current config to a specific place\nviper.WriteConfigAs(\"/path/to/my/.config\")\n\n// Will error since it has already been written\nviper.SafeWriteConfigAs(\"/path/to/my/.config\")\n\nviper.SafeWriteConfigAs(\"/path/to/my/.other_config\")\n```\n\nAs a rule of the thumb, methods prefixed with `Safe` won't overwrite any\nexisting file, while other methods will.\n\n### Watching and Re-reading Config Files\n\nGone are the days of needing to restart a server to have a config take\neffect--Viper powered applications can read an update to a config file while\nrunning and not miss a beat.\n\nIt's also possible to provide a function for Viper to run each time a change\noccurs.\n\n```go\n// All config paths must be defined prior to calling `WatchConfig()`\nviper.AddConfigPath(\"$HOME/.appname\")\n\nviper.OnConfigChange(func(e fsnotify.Event) {\n\tfmt.Println(\"Config file changed:\", e.Name)\n})\n\nviper.WatchConfig()\n```\n\n### Reading Config from `io.Reader`\n\nViper predefines many configuration sources but you can also implement your own\nrequired configuration source.\n\n```go\nviper.SetConfigType(\"yaml\")\n\nvar yamlExample = []byte(`\nhacker: true\nhobbies:\n- skateboarding\n- snowboarding\n- go\nname: steve\n`)\n\nviper.ReadConfig(bytes.NewBuffer(yamlExample))\n\nviper.Get(\"name\") // \"steve\"\n```\n\n### Setting Defaults\n\nA good configuration system will support default values, which are used if a\nkey hasn't been set in some other way.\n\nExamples:\n\n```go\nviper.SetDefault(\"ContentDir\", \"content\")\nviper.SetDefault(\"LayoutDir\", \"layouts\")\nviper.SetDefault(\"Taxonomies\", map[string]string{\"tag\": \"tags\", \"category\": \"categories\"})\n```\n\n### Setting Overrides\n\nViper allows explict setting of configuration, such as from your own\napplication logic.\n\n```go\nviper.Set(\"verbose\", true)\nviper.Set(\"host.port\", 5899) // Set an embedded key\n```\n\n### Registering and Using Aliases\n\nAliases permit a single value to be referenced by multiple keys\n\n```go\nviper.RegisterAlias(\"loud\", \"Verbose\")\n\nviper.Set(\"verbose\", true) // Same result as next line\nviper.Set(\"loud\", true)    // Same result as prior line\n\nviper.GetBool(\"loud\")    // true\nviper.GetBool(\"verbose\") // true\n```\n\n### Working with Environment Variables\n\nViper has full support for environment variables.\n\n> **NOTE** Unlike other configuration sources, environment variables are case\n> sensitive.\n\n```go\n// Tells Viper to use this prefix when reading environment variables\nviper.SetEnvPrefix(\"spf\")\n\n// Viper will look for \"SPF_ID\", automatically uppercasing the prefix and key\nviper.BindEnv(\"id\")\n\n// Alternatively, we can search for any environment variable prefixed and load\n// them in\nviper.AutomaticEnv()\n\nos.Setenv(\"SPF_ID\", \"13\")\n\nid := viper.Get(\"id\") // 13\n```\n\n* By default, empty environment variables are considered unset and will fall back to\n  the next configuration source, unless `AllowEmptyEnv` is used.\n* Viper does not \"cache\" environment variables--the value will be read each\n  time it is accessed.\n* `SetEnvKeyReplacer` and `EnvKeyReplacer` allow you to rewrite environment\n  variable keys, which is useful to merge SCREAMING_SNAKE_CASE environment\n  variables with kebab-cased configuration values from other sources.\n\n### Working with Flags\n\nViper has the ability to bind to flags. Specifically, Viper supports\n[pflag](https://github.com/spf13/pflag/) as used in the\n[Cobra](https://github.com/spf13/cobra) library.\n\nLike environment variables, the value is not set when the binding method is\ncalled, but when it is accessed.\n\nFor individual flags, the `BindPFlag` method provides this functionality.\n\n```go\nserverCmd.Flags().Int(\"port\", 1138, \"Port to run Application server on\")\n\nviper.BindPFlag(\"port\", serverCmd.Flags().Lookup(\"port\"))\n```\n\nYou can also bind an existing set of pflags.\n\n```go\npflag.Int(\"flagname\", 1234, \"help message for flagname\")\npflag.Parse()\n\nviper.BindPFlags(pflag.CommandLine)\n\ni := viper.GetInt(\"flagname\") // Retrieve values from viper instead of pflag\n```\n\nThe standard library [flag](https://golang.org/pkg/flag/) package is not\ndirectly supported, but may be parsed through pflag.\n\n```go\npackage main\n\nimport (\n\t\"flag\"\n\n\t\"github.com/spf13/pflag\"\n)\n\nfunc main() {\n\t// Using standard library \"flag\" package\n\tflag.Int(\"flagname\", 1234, \"help message for flagname\")\n\n    // Pass standard library flags to pflag\n\tpflag.CommandLine.AddGoFlagSet(flag.CommandLine)\n\tpflag.Parse()\n\n    // Viper takes over\n\tviper.BindPFlags(pflag.CommandLine)\n}\n```\n\nUse of pflag may be avoided entirely by implementing the `FlagValue` and\n`FlagValueSet` interfaces.\n\n```go\n// Implementing FlagValue\n\ntype myFlag struct {}\nfunc (f myFlag) HasChanged() bool { return false }\nfunc (f myFlag) Name() string { return \"my-flag-name\" }\nfunc (f myFlag) ValueString() string { return \"my-flag-value\" }\nfunc (f myFlag) ValueType() string { return \"string\" }\n\nviper.BindFlagValue(\"my-flag-name\", myFlag{})\n\n// Implementing FlagValueSet\n\ntype myFlagSet struct {\n\tflags []myFlag\n}\nfunc (f myFlagSet) VisitAll(fn func(FlagValue)) {\n\tfor _, flag := range flags {\n\t\tfn(flag)\n\t}\n}\n\nfSet := myFlagSet{\n\tflags: []myFlag{myFlag{}, myFlag{}},\n}\nviper.BindFlagValues(\"my-flags\", fSet)\n```\n\n### Remote Key/Value Store Support\n\nTo enable remote support in Viper, do a blank import of the `viper/remote`\npackage.\n\n```go\nimport _ \"github.com/spf13/viper/remote\"\n```\n\nViper supports the following remote key/value stores. Examples for each are\nprovided below.\n\n* Etcd and Etcd3\n* Consul\n* Firestore\n* NATS\n\nViper will read a config string retrieved from a path in a key/value store.\n\nViper supports multiple hosts separated by `;`. For example:\n`http://127.0.0.1:4001;http://127.0.0.1:4002`.\n\n#### Encryption\n\nViper uses [crypt](https://github.com/sagikazarmark/crypt) to retrieve\nconfiguration from the key/value store, which means that you can store your\nconfiguration values encrypted and have them automatically decrypted if you\nhave the correct GPG keyring. Encryption is optional.\n\nCrypt has a command-line helper that you can use to put configurations in your\nkey/value store.\n\n```bash\n$ go get github.com/sagikazarmark/crypt/bin/crypt\n$ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json\n$ crypt get -plaintext /config/hugo.json\n```\n\nSee the Crypt documentation for examples of how to set encrypted values, or\nhow to use Consul.\n\n### Remote Key/Value Store Examples (Unencrypted)\n\n#### etcd\n\n```go\nviper.AddRemoteProvider(\"etcd\", \"http://127.0.0.1:4001\",\"/config/hugo.json\")\nviper.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\"\nerr := viper.ReadRemoteConfig()\n```\n\n#### etcd3\n\n```go\nviper.AddRemoteProvider(\"etcd3\", \"http://127.0.0.1:4001\",\"/config/hugo.json\")\nviper.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\"\nerr := viper.ReadRemoteConfig()\n```\n\n#### Consul\n\nGiven a Consul key `MY_CONSUL_KEY` with the value:\n\n```json\n{\n    \"port\": 8080,\n    \"hostname\": \"myhostname.com\"\n}\n```\n\n```go\nviper.AddRemoteProvider(\"consul\", \"localhost:8500\", \"MY_CONSUL_KEY\")\nviper.SetConfigType(\"json\") // Need to explicitly set this to json\nerr := viper.ReadRemoteConfig()\n\nfmt.Println(viper.Get(\"port\")) // 8080\n```\n\n#### Firestore\n\n```go\nviper.AddRemoteProvider(\"firestore\", \"google-cloud-project-id\", \"collection/document\")\nviper.SetConfigType(\"json\") // Config's format: \"json\", \"toml\", \"yaml\", \"yml\"\nerr := viper.ReadRemoteConfig()\n```\n\nOf course, you're allowed to use `SecureRemoteProvider` also.\n\n#### NATS\n\n```go\nviper.AddRemoteProvider(\"nats\", \"nats://127.0.0.1:4222\", \"myapp.config\")\nviper.SetConfigType(\"json\")\nerr := viper.ReadRemoteConfig()\n```\n\n### Remote Key/Value Store Examples (Encrypted)\n\n```go\nviper.AddSecureRemoteProvider(\"etcd\",\"http://127.0.0.1:4001\",\"/config/hugo.json\",\"/etc/secrets/mykeyring.gpg\")\nviper.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\"\nerr := viper.ReadRemoteConfig()\n```\n\n### Watching Key/Value Store Changes\n\n```go\n// Alternatively, you can create a new viper instance\nvar runtime_viper = viper.New()\n\nruntime_viper.AddRemoteProvider(\"etcd\", \"http://127.0.0.1:4001\", \"/config/hugo.yml\")\nruntime_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\"\n\n// Read from remote config the first time\nerr := runtime_viper.ReadRemoteConfig()\n\n// Unmarshal config\nruntime_viper.Unmarshal(&runtime_conf)\n\n// Open a goroutine to watch remote changes forever\ngo func(){\n\tfor {\n\t\ttime.Sleep(time.Second * 5) // delay after each request\n\n\t\t// Currently, only tested with Etcd support\n\t\terr := runtime_viper.WatchRemoteConfig()\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"unable to read remote config: %v\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Unmarshal new config into our runtime config struct\n\t\truntime_viper.Unmarshal(&runtime_conf)\n\t}\n}()\n```\n\n## Getting Values From Viper\n\nThe simplest way to retrieve configuration values from Viper is to use `Get*`\nfunctions. `Get` will return an any type, but specific types may be retrieved\nwith `Get<Type>` functions.\n\nNote that each `Get*` function will return a zero value if it’s key is not\nfound. To check if a key exists, use the `IsSet` method.\n\nNested keys use `.` as a delimiter and numbers for array indexes. Given the\nfollowing configuration:\n\n```jsonc\n{\n    \"datastore\": {\n        \"metric\": {\n            \"host\": \"127.0.0.1\",\n            \"ports\": [\n                5799,\n                6029\n            ]\n        }\n    }\n}\n\n```\n\n```go\nGetString(\"datastore.metric.host\") // \"127.0.0.1\"\nGetInt(\"host.ports.1\") // 6029\n```\n\n> **NOTE** Viper _does not_ deep merge configuration values. Complex values\n> that are overridden will be entirely replaced.\n\nIf there exists a key that matches the delimited key path, its value will be\nreturned instead.\n\n```jsonc\n{\n    \"datastore.metric.host\": \"0.0.0.0\",\n    \"datastore\": {\n        \"metric\": {\n            \"host\": \"127.0.0.1\"\n        }\n    }\n}\n```\n\n```go\nGetString(\"datastore.metric.host\") // \"0.0.0.0\"\n```\n\n### Configuration Subsets\n\nIt's often useful to extract a subset of configuration (e.g., when developing a\nreusable module which should accept specific sections of configuration).\n\n```yaml\ncache:\n  cache1:\n    item-size: 64\n    max-items: 100\n  cache2:\n    item-size: 80\n    max-items: 200\n```\n\n```go\nfunc NewCache(v *Viper) *Cache {\n\treturn &Cache{\n\t\tItemSize: v.GetInt(\"item-size\"),\n\t\tMaxItems: v.GetInt(\"max-items\"),\n\t}\n}\n\ncache1Config := viper.Sub(\"cache.cache1\")\n\nif cache1Config == nil {\n    // Sub returns nil if the key cannot be found\n\tpanic(\"cache configuration not found\")\n}\n\ncache1 := NewCache(cache1Config)\n```\n\n### Unmarshaling\n\nYou also have the option of unmarshaling configuration to a struct, map, etc.,\nusing `Unmarshal*` methods.\n\n```go\ntype config struct {\n\tPort int\n\tName string\n\tPathMap string `mapstructure:\"path_map\"`\n}\n\nvar C config\n\nerr := viper.Unmarshal(&C)\nif err != nil {\n\tt.Fatalf(\"unable to decode into struct, %v\", err)\n}\n```\n\nIf you want to unmarshal configuration where the keys themselves contain `.`\n(the default key delimiter), you can change the delimiter.\n\n```go\nv := viper.NewWithOptions(viper.KeyDelimiter(\"::\"))\n\nv.SetDefault(\"chart::values\", map[string]any{\n\t\"ingress\": map[string]any{\n\t\t\"annotations\": map[string]any{\n\t\t\t\"traefik.frontend.rule.type\":                 \"PathPrefix\",\n\t\t\t\"traefik.ingress.kubernetes.io/ssl-redirect\": \"true\",\n\t\t},\n\t},\n})\n\ntype config struct {\n\tChart struct{\n\t\tValues map[string]any\n\t}\n}\n\nvar C config\n\nv.Unmarshal(&C)\n```\n\nViper also supports unmarshaling into embedded structs.\n\n```go\n/*\nExample config:\n\nmodule:\n    enabled: true\n    token: 89h3f98hbwf987h3f98wenf89ehf\n*/\ntype config struct {\n\tModule struct {\n\t\tEnabled bool\n\n\t\tmoduleConfig `mapstructure:\",squash\"`\n\t}\n}\n\ntype moduleConfig struct {\n\tToken string\n}\n\nvar C config\n\nerr := viper.Unmarshal(&C)\nif err != nil {\n\tt.Fatalf(\"unable to decode into struct, %v\", err)\n}\n```\n\nViper uses\n[github.com/go-viper/mapstructure](https://github.com/go-viper/mapstructure)\nunder the hood for unmarshaling values which uses `mapstructure` tags, by\ndefault.\n\n### Marshalling to String\n\nYou may need to marshal all the settings held in Viper into a string. You can\nuse your favorite format's marshaller with the config returned by\n`AllSettings`.\n\n```go\nimport (\n\tyaml \"go.yaml.in/yaml/v3\"\n)\n\nfunc yamlStringSettings() string {\n\tc := viper.AllSettings()\n\tbs, err := yaml.Marshal(c)\n\tif err != nil {\n\t\tlog.Fatalf(\"unable to marshal config to YAML: %v\", err)\n\t}\n\treturn string(bs)\n}\n```\n\n### Decoding Custom Formats\n\nA frequently requested feature is adding more value formats and decoders (for\nexample; parsing character delimited strings into slices. This is already\navailable in Viper using mapstructure decode hooks.\n\nRead more in [this blog\npost](https://sagikazarmark.hu/blog/decoding-custom-formats-with-viper/).\n\n\n## FAQ\n\n### Why is it called “Viper”?\n\nViper is designed to be a\n[companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe)) to\n[Cobra](https://github.com/spf13/cobra). While both can operate completely\nindependently, together they make a powerful pair to handle much of your\napplication foundation needs.\n\n### I found a bug or want a feature, should I file an issue or a PR?\n\nYes, but there are two things to be aware of.\n\n1.  The Viper project is currently prioritizing backwards compatibility and\n    stability over features.\n2.  Features may be deferred until Viper 2 forms.\n\n### Can multiple Viper instances be used?\n\n**tl;dr:** Yes.\n\nEach will have its own unique configuration and can read from a different\nconfiguration source. All of the functions that the Viper package supports are\nmirrored as methods on a Viper instance.\n\n```go\nx := viper.New()\ny := viper.New()\n\nx.SetDefault(\"ContentDir\", \"content\")\ny.SetDefault(\"ContentDir\", \"foobar\")\n```\n\n### Should Viper be a global singleton or passed around?\n\nThe best practice is to initialize a Viper instance and pass that around when\nnecessary.\n\nViper comes with a global instance (singleton) out of the box. Although it\nmakes setting up configuration easy, using it is generally discouraged as it\nmakes testing harder and can lead to unexpected behavior.\n\nThe global instance may be deprecated in the future. See\n[#1855](https://github.com/spf13/viper/issues/1855) for more details.\n\n### Does Viper support case sensitive keys?\n\n**tl;dr:** No.\n\nViper merges configuration from various sources, many of which are either case\ninsensitive or use different casing than other sources (e.g., env vars). In\norder to provide the best experience when using multiple sources, all keys are\nmade case insensitive.\n\nThere has been several attempts to implement case sensitivity, but\nunfortunately it's not trivial. We might take a stab at implementing it in\n[Viper v2](https://github.com/spf13/viper/issues/772), but despite the initial\nnoise, it does not seem to be requested that much.\n\nYou can vote for case sensitivity by filling out this feedback form:\nhttps://forms.gle/R6faU74qPRPAzchZ9.\n\n### Is it safe to concurrently read and write to a Viper instance?\n\nNo, you will need to synchronize access to Viper yourself (for example by using\nthe `sync` package). Concurrent reads and writes can cause a panic.\n\n\n## Troubleshooting\n\nSee [TROUBLESHOOTING.md](TROUBLESHOOTING.md).\n\n\n## Development\n\n**For an optimal developer experience, it is recommended to install\n[Nix](https://nixos.org/download.html) and\n[direnv](https://direnv.net/docs/installation.html).**\n\n_Alternatively, install [Go](https://go.dev/dl/) on your computer then run\n`make deps` to install the rest of the dependencies._\n\nRun the test suite:\n\n```shell\nmake test\n```\n\nRun linters:\n\n```shell\nmake lint # pass -j option to run them in parallel\n```\n\nSome linter violations can automatically be fixed:\n\n```shell\nmake fmt\n```\n\n\n## License\n\nThe project is licensed under the [MIT License](LICENSE).\n"
  },
  {
    "path": "TROUBLESHOOTING.md",
    "content": "# Troubleshooting\n\n## Unmarshaling doesn't work\n\nThe 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.\n\n## Cannot find package\n\nViper installation seems to fail a lot lately with the following (or a similar) error:\n\n```\ncannot find package \"github.com/hashicorp/hcl/tree/hcl1\" in any of:\n/usr/local/Cellar/go/1.15.7_1/libexec/src/github.com/hashicorp/hcl/tree/hcl1 (from $GOROOT)\n/Users/user/go/src/github.com/hashicorp/hcl/tree/hcl1 (from $GOPATH)\n```\n\nAs the error message suggests, Go tries to look up dependencies in `GOPATH` mode (as it's commonly called) from the `GOPATH`.\nViper 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).\n\nThe solution is easy: switch to using Go Modules.\nPlease refer to the [wiki](https://go.dev/wiki/Modules) on how to do that.\n\n**tl;dr* `export GO111MODULE=on`\n\n## Unquoted 'y' and 'n' characters get replaced with _true_ and _false_ when reading a YAML file\n\nThis is a YAML 1.1 feature according to [go-yaml/yaml#740](https://github.com/go-yaml/yaml/issues/740).\n\nPotential solutions are:\n\n1. Quoting values resolved as boolean\n1. Upgrading to YAML v3 (for the time being this is possible by passing the `viper_yaml3` tag to your build)\n"
  },
  {
    "path": "UPGRADE.md",
    "content": "# Update Log\n\n**This document details any major updates required to use new features or improvements in Viper.**\n\n## v1.20.x\n\n### New file searching API\n\nViper now includes a new file searching API that allows users to customize how Viper looks for config files.\n\nViper accepts a custom [`Finder`](https://pkg.go.dev/github.com/spf13/viper#Finder) interface implementation:\n\n```go\n// Finder looks for files and directories in an [afero.Fs] filesystem.\ntype Finder interface {\n\tFind(fsys afero.Fs) ([]string, error)\n}\n```\n\nIt is supposed to return a list of paths to config files.\n\nThe default implementation uses [github.com/sagikazarmark/locafero](https://github.com/sagikazarmark/locafero) under the hood.\n\nYou can supply your own implementation using `WithFinder`:\n\n```go\nv := viper.NewWithOptions(\n    viper.WithFinder(&MyFinder{}),\n)\n```\n\nFor more information, check out the [Finder examples](https://pkg.go.dev/github.com/spf13/viper#Finder)\nand the [documentation](https://pkg.go.dev/github.com/sagikazarmark/locafero) for the locafero package.\n\n### New encoding API\n\nViper now allows customizing the encoding layer by providing an API for encoding and decoding configuration data:\n\n```go\n// Encoder encodes Viper's internal data structures into a byte representation.\n// It's primarily used for encoding a map[string]any into a file format.\ntype Encoder interface {\n\tEncode(v map[string]any) ([]byte, error)\n}\n\n// Decoder decodes the contents of a byte slice into Viper's internal data structures.\n// It's primarily used for decoding contents of a file into a map[string]any.\ntype Decoder interface {\n\tDecode(b []byte, v map[string]any) error\n}\n\n// Codec combines [Encoder] and [Decoder] interfaces.\ntype Codec interface {\n\tEncoder\n\tDecoder\n}\n```\n\nBy default, Viper includes the following codecs:\n\n- JSON\n- TOML\n- YAML\n- Dotenv\n\nThe rest of the codecs are moved to [github.com/go-viper/encoding](https://github.com/go-viper/encoding)\n\nCustomizing the encoding layer is possible by providing a custom registry of codecs:\n\n- [Encoder](https://pkg.go.dev/github.com/spf13/viper#Encoder) -> [EncoderRegistry](https://pkg.go.dev/github.com/spf13/viper#EncoderRegistry)\n- [Decoder](https://pkg.go.dev/github.com/spf13/viper#Decoder) -> [DecoderRegistry](https://pkg.go.dev/github.com/spf13/viper#DecoderRegistry)\n- [Codec](https://pkg.go.dev/github.com/spf13/viper#Codec) -> [CodecRegistry](https://pkg.go.dev/github.com/spf13/viper#CodecRegistry)\n\nYou can supply the registry of codecs to Viper using the appropriate `With*Registry` function:\n\n```go\ncodecRegistry := viper.NewCodecRegistry()\n\ncodecRegistry.RegisterCodec(\"myformat\", &MyCodec{})\n\nv := viper.NewWithOptions(\n    viper.WithCodecRegistry(codecRegistry),\n)\n```\n\n### BREAKING: \"github.com/mitchellh/mapstructure\" depedency replaced\n\nThe 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)).\n\nAs a result, the package import path needs to be changed in cases where `mapstructure` is directly referenced in your code.\n\nFor example, when providing a custom decoder config:\n\n```go\nerr := viper.Unmarshal(&appConfig, func(config *mapstructure.DecoderConfig) {\n\tconfig.TagName = \"yaml\"\n})\n```\n\nThe change is fairly straightforward, just replace all occurrences of the import path `github.com/mitchellh/mapstructure` with `github.com/go-viper/mapstructure/v2`:\n\n```diff\n- import \"github.com/mitchellh/mapstructure\"\n+ import \"github.com/go-viper/mapstructure/v2\"\n```\n\n### BREAKING: HCL, Java properties, INI removed from core\n\nIn order to reduce third-party dependencies, Viper dropped support for the following formats from the core:\n\n- HCL\n- Java properties\n- INI\n\nYou can still use these formats though by importing them from [github.com/go-viper/encoding](https://github.com/go-viper/encoding):\n\n```go\nimport (\n    \"github.com/go-viper/encoding/hcl\"\n    \"github.com/go-viper/encoding/javaproperties\"\n    \"github.com/go-viper/encoding/ini\"\n)\n\ncodecRegistry := viper.NewCodecRegistry()\n\n{\n    codec := hcl.Codec{}\n\n    codecRegistry.RegisterCodec(\"hcl\", codec)\n    codecRegistry.RegisterCodec(\"tfvars\", codec)\n\n}\n\n{\n    codec := &javaproperties.Codec{}\n\n    codecRegistry.RegisterCodec(\"properties\", codec)\n    codecRegistry.RegisterCodec(\"props\", codec)\n    codecRegistry.RegisterCodec(\"prop\", codec)\n}\n\ncodecRegistry.RegisterCodec(\"ini\", ini.Codec{})\n\nv := viper.NewWithOptions(\n    viper.WithCodecRegistry(codecRegistry),\n)\n```\n"
  },
  {
    "path": "encoding.go",
    "content": "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/spf13/viper/internal/encoding/json\"\n\t\"github.com/spf13/viper/internal/encoding/toml\"\n\t\"github.com/spf13/viper/internal/encoding/yaml\"\n)\n\n// Encoder encodes Viper's internal data structures into a byte representation.\n// It's primarily used for encoding a map[string]any into a file format.\ntype Encoder interface {\n\tEncode(v map[string]any) ([]byte, error)\n}\n\n// Decoder decodes the contents of a byte slice into Viper's internal data structures.\n// It's primarily used for decoding contents of a file into a map[string]any.\ntype Decoder interface {\n\tDecode(b []byte, v map[string]any) error\n}\n\n// Codec combines [Encoder] and [Decoder] interfaces.\ntype Codec interface {\n\tEncoder\n\tDecoder\n}\n\n// TODO: consider adding specific errors for not found scenarios\n\n// EncoderRegistry returns an [Encoder] for a given format.\n//\n// Format is case-insensitive.\n//\n// [EncoderRegistry] returns an error if no [Encoder] is registered for the format.\ntype EncoderRegistry interface {\n\tEncoder(format string) (Encoder, error)\n}\n\n// DecoderRegistry returns an [Decoder] for a given format.\n//\n// Format is case-insensitive.\n//\n// [DecoderRegistry] returns an error if no [Decoder] is registered for the format.\ntype DecoderRegistry interface {\n\tDecoder(format string) (Decoder, error)\n}\n\n// CodecRegistry combines [EncoderRegistry] and [DecoderRegistry] interfaces.\ntype CodecRegistry interface {\n\tEncoderRegistry\n\tDecoderRegistry\n}\n\n// WithEncoderRegistry sets a custom [EncoderRegistry].\nfunc WithEncoderRegistry(r EncoderRegistry) Option {\n\treturn optionFunc(func(v *Viper) {\n\t\tif r == nil {\n\t\t\treturn\n\t\t}\n\n\t\tv.encoderRegistry = r\n\t})\n}\n\n// WithDecoderRegistry sets a custom [DecoderRegistry].\nfunc WithDecoderRegistry(r DecoderRegistry) Option {\n\treturn optionFunc(func(v *Viper) {\n\t\tif r == nil {\n\t\t\treturn\n\t\t}\n\n\t\tv.decoderRegistry = r\n\t})\n}\n\n// WithCodecRegistry sets a custom [EncoderRegistry] and [DecoderRegistry].\nfunc WithCodecRegistry(r CodecRegistry) Option {\n\treturn optionFunc(func(v *Viper) {\n\t\tif r == nil {\n\t\t\treturn\n\t\t}\n\n\t\tv.encoderRegistry = r\n\t\tv.decoderRegistry = r\n\t})\n}\n\n// DefaultCodecRegistry is a simple implementation of [CodecRegistry] that allows registering custom [Codec]s.\ntype DefaultCodecRegistry struct {\n\tcodecs map[string]Codec\n\n\tmu   sync.RWMutex\n\tonce sync.Once\n}\n\n// NewCodecRegistry returns a new [CodecRegistry], ready to accept custom [Codec]s.\nfunc NewCodecRegistry() *DefaultCodecRegistry {\n\tr := &DefaultCodecRegistry{}\n\n\tr.init()\n\n\treturn r\n}\n\nfunc (r *DefaultCodecRegistry) init() {\n\tr.once.Do(func() {\n\t\tr.codecs = map[string]Codec{}\n\t})\n}\n\n// RegisterCodec registers a custom [Codec].\n//\n// Format is case-insensitive.\nfunc (r *DefaultCodecRegistry) RegisterCodec(format string, codec Codec) error {\n\tr.init()\n\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\tr.codecs[strings.ToLower(format)] = codec\n\n\treturn nil\n}\n\n// Encoder implements the [EncoderRegistry] interface.\n//\n// Format is case-insensitive.\nfunc (r *DefaultCodecRegistry) Encoder(format string) (Encoder, error) {\n\tencoder, ok := r.codec(format)\n\tif !ok {\n\t\treturn nil, errors.New(\"encoder not found for this format\")\n\t}\n\n\treturn encoder, nil\n}\n\n// Decoder implements the [DecoderRegistry] interface.\n//\n// Format is case-insensitive.\nfunc (r *DefaultCodecRegistry) Decoder(format string) (Decoder, error) {\n\tdecoder, ok := r.codec(format)\n\tif !ok {\n\t\treturn nil, errors.New(\"decoder not found for this format\")\n\t}\n\n\treturn decoder, nil\n}\n\nfunc (r *DefaultCodecRegistry) codec(format string) (Codec, bool) {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\tformat = strings.ToLower(format)\n\n\tif r.codecs != nil {\n\t\tcodec, ok := r.codecs[format]\n\t\tif ok {\n\t\t\treturn codec, true\n\t\t}\n\t}\n\n\tswitch format {\n\tcase \"yaml\", \"yml\":\n\t\treturn yaml.Codec{}, true\n\n\tcase \"json\":\n\t\treturn json.Codec{}, true\n\n\tcase \"toml\":\n\t\treturn toml.Codec{}, true\n\n\tcase \"dotenv\", \"env\":\n\t\treturn &dotenv.Codec{}, true\n\t}\n\n\treturn nil, false\n}\n"
  },
  {
    "path": "encoding_test.go",
    "content": "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 codec struct{}\n\nfunc (codec) Encode(_ map[string]any) ([]byte, error) {\n\treturn nil, nil\n}\n\nfunc (codec) Decode(_ []byte, _ map[string]any) error {\n\treturn nil\n}\n\nfunc TestDefaultCodecRegistry(t *testing.T) {\n\tt.Run(\"OK\", func(t *testing.T) {\n\t\tregistry := NewCodecRegistry()\n\n\t\tc := codec{}\n\n\t\terr := registry.RegisterCodec(\"myformat\", c)\n\t\trequire.NoError(t, err)\n\n\t\tencoder, err := registry.Encoder(\"myformat\")\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, c, encoder)\n\n\t\tdecoder, err := registry.Decoder(\"myformat\")\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, c, decoder)\n\t})\n\n\tt.Run(\"CodecNotFound\", func(t *testing.T) {\n\t\tregistry := NewCodecRegistry()\n\n\t\t_, err := registry.Encoder(\"myformat\")\n\t\trequire.Error(t, err)\n\n\t\t_, err = registry.Decoder(\"myformat\")\n\t\trequire.Error(t, err)\n\t})\n\n\tt.Run(\"FormatIsCaseInsensitive\", func(t *testing.T) {\n\t\tregistry := NewCodecRegistry()\n\n\t\tc := codec{}\n\n\t\terr := registry.RegisterCodec(\"MYFORMAT\", c)\n\t\trequire.NoError(t, err)\n\n\t\t{\n\t\t\tencoder, err := registry.Encoder(\"myformat\")\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, c, encoder)\n\t\t}\n\n\t\t{\n\t\t\tencoder, err := registry.Encoder(\"MYFORMAT\")\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, c, encoder)\n\t\t}\n\n\t\t{\n\t\t\tdecoder, err := registry.Decoder(\"myformat\")\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, c, decoder)\n\t\t}\n\n\t\t{\n\t\t\tdecoder, err := registry.Decoder(\"MYFORMAT\")\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, c, decoder)\n\t\t}\n\t})\n\n\tt.Run(\"OverrideDefault\", func(t *testing.T) {\n\t\tregistry := NewCodecRegistry()\n\n\t\tc := codec{}\n\n\t\terr := registry.RegisterCodec(\"yaml\", c)\n\t\trequire.NoError(t, err)\n\n\t\tencoder, err := registry.Encoder(\"yaml\")\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, c, encoder)\n\n\t\tdecoder, err := registry.Decoder(\"yaml\")\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, c, decoder)\n\t})\n}\n"
  },
  {
    "path": "errors.go",
    "content": "package viper\n\nimport (\n\t\"fmt\"\n)\n\n// FileLookupError is returned when Viper cannot resolve a configuration file.\n//\n// This is meant to be a common interface for all file look-up errors, occurring either because a\n// file does not exist or because it cannot find any file matching finder criteria.\ntype FileLookupError interface {\n\terror\n\n\tfileLookup()\n}\n\n// ConfigFileNotFoundError denotes failing to find a configuration file from a search.\n//\n// Deprecated: This is error wraps [FileNotFoundFromSearchError], which should be used instead.\ntype ConfigFileNotFoundError struct {\n\tlocations []string\n\tname      string\n}\n\n// Error returns the formatted error.\nfunc (e ConfigFileNotFoundError) Error() string {\n\treturn e.Unwrap().Error()\n}\n\n// Unwraps to FileNotFoundFromSearchError.\nfunc (e ConfigFileNotFoundError) Unwrap() error {\n\treturn FileNotFoundFromSearchError(e)\n}\n\n// FileNotFoundFromSearchError denotes failing to find a configuration file from a search.\n// Wraps ConfigFileNotFoundError.\ntype FileNotFoundFromSearchError struct {\n\tlocations []string\n\tname      string\n}\n\nfunc (e FileNotFoundFromSearchError) fileLookup() {}\n\n// Error returns the formatted error.\nfunc (e FileNotFoundFromSearchError) Error() string {\n\tmessage := fmt.Sprintf(\"File %q not found\", e.name)\n\n\tif len(e.locations) > 0 {\n\t\tmessage += fmt.Sprintf(\" in %v\", e.locations)\n\t}\n\n\treturn message\n}\n\n// FileNotFoundError denotes failing to find a specific configuration file.\ntype FileNotFoundError struct {\n\terr  error\n\tpath string\n}\n\nfunc (e FileNotFoundError) fileLookup() {}\n\n// Error returns the formatted error.\nfunc (e FileNotFoundError) Error() string {\n\treturn fmt.Sprintf(\"file not found: %s\", e.path)\n}\n\n// ConfigFileAlreadyExistsError denotes failure to write new configuration file.\ntype ConfigFileAlreadyExistsError string\n\n// Error returns the formatted error when configuration already exists.\nfunc (e ConfigFileAlreadyExistsError) Error() string {\n\treturn fmt.Sprintf(\"Config File %q Already Exists\", string(e))\n}\n\n// ConfigMarshalError happens when failing to marshal the configuration.\ntype ConfigMarshalError struct {\n\terr error\n}\n\n// Error returns the formatted configuration error.\nfunc (e ConfigMarshalError) Error() string {\n\treturn fmt.Sprintf(\"While marshaling config: %s\", e.err.Error())\n}\n\n// UnsupportedConfigError denotes encountering an unsupported\n// configuration filetype.\ntype UnsupportedConfigError string\n\n// Error returns the formatted configuration error.\nfunc (str UnsupportedConfigError) Error() string {\n\treturn fmt.Sprintf(\"Unsupported Config Type %q\", string(str))\n}\n"
  },
  {
    "path": "experimental.go",
    "content": "package viper\n\n// ExperimentalBindStruct tells Viper to use the new bind struct feature.\nfunc ExperimentalBindStruct() Option {\n\treturn optionFunc(func(v *Viper) {\n\t\tv.experimentalBindStruct = true\n\t})\n}\n"
  },
  {
    "path": "file.go",
    "content": "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// ExperimentalFinder tells Viper to use the new Finder interface for finding configuration files.\nfunc ExperimentalFinder() Option {\n\treturn optionFunc(func(v *Viper) {\n\t\tv.experimentalFinder = true\n\t})\n}\n\n// Search for a config file.\nfunc (v *Viper) findConfigFile() (string, error) {\n\tfinder := v.finder\n\n\tif finder == nil && v.experimentalFinder {\n\t\tvar names []string\n\n\t\tif v.configType != \"\" {\n\t\t\tnames = locafero.NameWithOptionalExtensions(v.configName, SupportedExts...)\n\t\t} else {\n\t\t\tnames = locafero.NameWithExtensions(v.configName, SupportedExts...)\n\t\t}\n\n\t\tfinder = locafero.Finder{\n\t\t\tPaths: v.configPaths,\n\t\t\tNames: names,\n\t\t\tType:  locafero.FileTypeFile,\n\t\t}\n\t}\n\n\tif finder != nil {\n\t\treturn v.findConfigFileWithFinder(finder)\n\t}\n\n\treturn v.findConfigFileOld()\n}\n\nfunc (v *Viper) findConfigFileWithFinder(finder Finder) (string, error) {\n\tresults, err := finder.Find(v.fs)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif len(results) == 0 {\n\t\treturn \"\", ConfigFileNotFoundError{name: v.configName, locations: v.configPaths}\n\t}\n\n\t// We call clean on the final result to ensure that the path is in its canonical form.\n\t// This is mostly for consistent path handling and to make sure tests pass.\n\treturn results[0], nil\n}\n\n// Search all configPaths for any config file.\n// Returns the first path that exists (and is a config file).\nfunc (v *Viper) findConfigFileOld() (string, error) {\n\tv.logger.Info(\"searching for config in paths\", \"paths\", v.configPaths)\n\n\tfor _, cp := range v.configPaths {\n\t\tfile := v.searchInPath(cp)\n\t\tif file != \"\" {\n\t\t\treturn file, nil\n\t\t}\n\t}\n\treturn \"\", ConfigFileNotFoundError{name: v.configName, locations: v.configPaths}\n}\n\nfunc (v *Viper) searchInPath(in string) (filename string) {\n\tv.logger.Debug(\"searching for config in path\", \"path\", in)\n\tfor _, ext := range SupportedExts {\n\t\tv.logger.Debug(\"checking if file exists\", \"file\", filepath.Join(in, v.configName+\".\"+ext))\n\t\tif b, _ := exists(v.fs, filepath.Join(in, v.configName+\".\"+ext)); b {\n\t\t\tv.logger.Debug(\"found file\", \"file\", filepath.Join(in, v.configName+\".\"+ext))\n\t\t\treturn filepath.Join(in, v.configName+\".\"+ext)\n\t\t}\n\t}\n\n\tif v.configType != \"\" {\n\t\tif b, _ := exists(v.fs, filepath.Join(in, v.configName)); b {\n\t\t\treturn filepath.Join(in, v.configName)\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\n// exists checks if file exists.\nfunc exists(fs afero.Fs, path string) (bool, error) {\n\tstat, err := fs.Stat(path)\n\tif err == nil {\n\t\treturn !stat.IsDir(), nil\n\t}\n\tif os.IsNotExist(err) {\n\t\treturn false, nil\n\t}\n\treturn false, err\n}\n"
  },
  {
    "path": "finder.go",
    "content": "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 Finder) Option {\n\treturn optionFunc(func(v *Viper) {\n\t\tif f == nil {\n\t\t\treturn\n\t\t}\n\n\t\tv.finder = f\n\t})\n}\n\n// Finder looks for files and directories in an [afero.Fs] filesystem.\ntype Finder interface {\n\tFind(fsys afero.Fs) ([]string, error)\n}\n\n// Finders combines multiple finders into one.\nfunc Finders(finders ...Finder) Finder {\n\treturn &combinedFinder{finders: finders}\n}\n\n// combinedFinder is a Finder that combines multiple finders.\ntype combinedFinder struct {\n\tfinders []Finder\n}\n\n// Find implements the [Finder] interface.\nfunc (c *combinedFinder) Find(fsys afero.Fs) ([]string, error) {\n\tvar results []string\n\tvar errs []error\n\n\tfor _, finder := range c.finders {\n\t\tif finder == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tr, err := finder.Find(fsys)\n\t\tif err != nil {\n\t\t\terrs = append(errs, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tresults = append(results, r...)\n\t}\n\n\treturn results, errors.Join(errs...)\n}\n"
  },
  {
    "path": "finder_example_test.go",
    "content": "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/viper\"\n)\n\nfunc ExampleFinder() {\n\tfs := afero.NewMemMapFs()\n\n\tfs.Mkdir(\"/home/user\", 0o777)\n\n\tf, _ := fs.Create(\"/home/user/myapp.yaml\")\n\tf.WriteString(\"foo: bar\")\n\tf.Close()\n\n\t// HCL will have a \"lower\" priority in the search order\n\tfs.Create(\"/home/user/myapp.hcl\")\n\n\tfinder := locafero.Finder{\n\t\tPaths: []string{\"/home/user\"},\n\t\tNames: locafero.NameWithExtensions(\"myapp\", viper.SupportedExts...),\n\t\tType:  locafero.FileTypeFile, // This is important!\n\t}\n\n\tv := viper.NewWithOptions(viper.WithFinder(finder))\n\tv.SetFs(fs)\n\tv.ReadInConfig()\n\n\tfmt.Println(v.GetString(\"foo\"))\n\n\t// Output:\n\t// bar\n}\n\nfunc ExampleFinders() {\n\tfs := afero.NewMemMapFs()\n\n\tfs.Mkdir(\"/home/user\", 0o777)\n\tf, _ := fs.Create(\"/home/user/myapp.yaml\")\n\tf.WriteString(\"foo: bar\")\n\tf.Close()\n\n\tfs.Mkdir(\"/etc/myapp\", 0o777)\n\tfs.Create(\"/etc/myapp/config.yaml\")\n\n\t// Combine multiple finders to search for files in multiple locations with different criteria\n\tfinder := viper.Finders(\n\t\tlocafero.Finder{\n\t\t\tPaths: []string{\"/home/user\"},\n\t\t\tNames: locafero.NameWithExtensions(\"myapp\", viper.SupportedExts...),\n\t\t\tType:  locafero.FileTypeFile, // This is important!\n\t\t},\n\t\tlocafero.Finder{\n\t\t\tPaths: []string{\"/etc/myapp\"},\n\t\t\tNames: []string{\"config.yaml\"}, // Only accept YAML files in the system config directory\n\t\t\tType:  locafero.FileTypeFile,   // This is important!\n\t\t},\n\t)\n\n\tv := viper.NewWithOptions(viper.WithFinder(finder))\n\tv.SetFs(fs)\n\tv.ReadInConfig()\n\n\tfmt.Println(v.GetString(\"foo\"))\n\n\t// Output:\n\t// bar\n}\n"
  },
  {
    "path": "finder_test.go",
    "content": "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/stretchr/testify/require\"\n)\n\ntype finderStub struct {\n\tresults []string\n}\n\nfunc (f finderStub) Find(_ afero.Fs) ([]string, error) {\n\treturn f.results, nil\n}\n\nfunc TestFinders(t *testing.T) {\n\tfinder := Finders(\n\t\tfinderStub{\n\t\t\tresults: []string{\n\t\t\t\t\"/home/user/.viper.yaml\",\n\t\t\t},\n\t\t},\n\t\tfinderStub{\n\t\t\tresults: []string{\n\t\t\t\t\"/etc/viper/config.yaml\",\n\t\t\t},\n\t\t},\n\t)\n\n\tresults, err := finder.Find(afero.NewMemMapFs())\n\trequire.NoError(t, err)\n\n\texpected := []string{\n\t\t\"/home/user/.viper.yaml\",\n\t\t\"/etc/viper/config.yaml\",\n\t}\n\n\tassert.Equal(t, expected, results)\n}\n"
  },
  {
    "path": "flags.go",
    "content": "package viper\n\nimport \"github.com/spf13/pflag\"\n\n// FlagValueSet is an interface that users can implement\n// to bind a set of flags to viper.\ntype FlagValueSet interface {\n\tVisitAll(fn func(FlagValue))\n}\n\n// FlagValue is an interface that users can implement\n// to bind different flags to viper.\ntype FlagValue interface {\n\tHasChanged() bool\n\tName() string\n\tValueString() string\n\tValueType() string\n}\n\n// pflagValueSet is a wrapper around *pflag.ValueSet\n// that implements FlagValueSet.\ntype pflagValueSet struct {\n\tflags *pflag.FlagSet\n}\n\n// VisitAll iterates over all *pflag.Flag inside the *pflag.FlagSet.\nfunc (p pflagValueSet) VisitAll(fn func(flag FlagValue)) {\n\tp.flags.VisitAll(func(flag *pflag.Flag) {\n\t\tfn(pflagValue{flag})\n\t})\n}\n\n// pflagValue is a wrapper around *pflag.flag\n// that implements FlagValue.\ntype pflagValue struct {\n\tflag *pflag.Flag\n}\n\n// HasChanged returns whether the flag has changes or not.\nfunc (p pflagValue) HasChanged() bool {\n\treturn p.flag.Changed\n}\n\n// Name returns the name of the flag.\nfunc (p pflagValue) Name() string {\n\treturn p.flag.Name\n}\n\n// ValueString returns the value of the flag as a string.\nfunc (p pflagValue) ValueString() string {\n\treturn p.flag.Value.String()\n}\n\n// ValueType returns the type of the flag as a string.\nfunc (p pflagValue) ValueType() string {\n\treturn p.flag.Value.Type()\n}\n"
  },
  {
    "path": "flags_test.go",
    "content": "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/stretchr/testify/require\"\n)\n\nfunc TestBindFlagValueSet(t *testing.T) {\n\tReset()\n\tflagSet := pflag.NewFlagSet(\"test\", pflag.ContinueOnError)\n\n\ttestValues := map[string]*string{\n\t\t\"host\":     nil,\n\t\t\"port\":     nil,\n\t\t\"endpoint\": nil,\n\t}\n\n\tmutatedTestValues := map[string]string{\n\t\t\"host\":     \"localhost\",\n\t\t\"port\":     \"6060\",\n\t\t\"endpoint\": \"/public\",\n\t}\n\n\tfor name := range testValues {\n\t\ttestValues[name] = flagSet.String(name, \"\", \"test\")\n\t}\n\n\tflagValueSet := pflagValueSet{flagSet}\n\n\terr := BindFlagValues(flagValueSet)\n\trequire.NoError(t, err, \"error binding flag set\")\n\n\tflagSet.VisitAll(func(flag *pflag.Flag) {\n\t\tflag.Value.Set(mutatedTestValues[flag.Name])\n\t\tflag.Changed = true\n\t})\n\n\tfor name, expected := range mutatedTestValues {\n\t\tassert.Equal(t, expected, Get(name))\n\t}\n}\n\nfunc TestBindFlagValue(t *testing.T) {\n\ttestString := \"testing\"\n\ttestValue := newStringValue(testString, &testString)\n\n\tflag := &pflag.Flag{\n\t\tName:    \"testflag\",\n\t\tValue:   testValue,\n\t\tChanged: false,\n\t}\n\n\tflagValue := pflagValue{flag}\n\tBindFlagValue(\"testvalue\", flagValue)\n\n\tassert.Equal(t, testString, Get(\"testvalue\"))\n\n\tflag.Value.Set(\"testing_mutate\")\n\tflag.Changed = true // hack for pflag usage\n\n\tassert.Equal(t, \"testing_mutate\", Get(\"testvalue\"))\n}\n"
  },
  {
    "path": "flake.nix",
    "content": "{\n  description = \"Viper\";\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixpkgs-unstable\";\n    flake-parts.url = \"github:hercules-ci/flake-parts\";\n    devenv.url = \"github:cachix/devenv\";\n  };\n\n  outputs =\n    inputs@{ flake-parts, ... }:\n    flake-parts.lib.mkFlake { inherit inputs; } {\n      imports = [\n        inputs.devenv.flakeModule\n      ];\n\n      systems = [\n        \"x86_64-linux\"\n        \"x86_64-darwin\"\n        \"aarch64-darwin\"\n      ];\n\n      perSystem =\n        { pkgs, ... }:\n        {\n          devenv.shells = {\n            default = {\n              languages = {\n                go.enable = true;\n              };\n\n              git-hooks.hooks = {\n                nixpkgs-fmt.enable = true;\n                yamllint.enable = true;\n              };\n\n              packages = with pkgs; [\n                gnumake\n\n                golangci-lint\n                yamllint\n              ];\n\n              scripts = {\n                versions.exec = ''\n                  go version\n                  golangci-lint version\n                '';\n              };\n\n              enterShell = ''\n                versions\n              '';\n\n              # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767\n              containers = pkgs.lib.mkForce { };\n            };\n          };\n        };\n    };\n}\n"
  },
  {
    "path": "go.mod",
    "content": "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/mapstructure/v2 v2.4.0\n\tgithub.com/pelletier/go-toml/v2 v2.2.4\n\tgithub.com/sagikazarmark/locafero v0.12.0\n\tgithub.com/spf13/afero v1.15.0\n\tgithub.com/spf13/cast v1.10.0\n\tgithub.com/spf13/pflag v1.0.10\n\tgithub.com/stretchr/testify v1.11.1\n\tgithub.com/subosito/gotenv v1.6.0\n\tgo.yaml.in/yaml/v3 v3.0.4\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/google/go-cmp v0.6.0 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgolang.org/x/sys v0.29.0 // indirect\n\tgolang.org/x/text v0.28.0 // indirect\n\tgopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=\ngithub.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=\ngithub.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=\ngithub.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=\ngithub.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=\ngithub.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=\ngithub.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=\ngithub.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=\ngithub.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngolang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=\ngolang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=\ngolang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "internal/encoding/dotenv/codec.go",
    "content": "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// Codec implements the encoding.Encoder and encoding.Decoder interfaces for encoding data containing environment variables\n// (commonly called as dotenv format).\ntype Codec struct{}\n\n// Encode encodes a map[string]any into a dotenv byte slice.\nfunc (Codec) Encode(v map[string]any) ([]byte, error) {\n\tflattened := map[string]any{}\n\n\tflattened = flattenAndMergeMap(flattened, v, \"\", keyDelimiter)\n\n\tkeys := make([]string, 0, len(flattened))\n\n\tfor key := range flattened {\n\t\tkeys = append(keys, key)\n\t}\n\n\tsort.Strings(keys)\n\n\tvar buf bytes.Buffer\n\n\tfor _, key := range keys {\n\t\t_, err := buf.WriteString(fmt.Sprintf(\"%v=%v\\n\", strings.ToUpper(key), flattened[key]))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\n// Decode decodes a dotenv byte slice into a map[string]any.\nfunc (Codec) Decode(b []byte, v map[string]any) error {\n\tvar buf bytes.Buffer\n\n\t_, err := buf.Write(b)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tenv, err := gotenv.StrictParse(&buf)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor key, value := range env {\n\t\tv[key] = value\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/encoding/dotenv/codec_test.go",
    "content": "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// original form of the data.\nconst original = `# key-value pair\nKEY=value\n`\n\n// encoded form of the data.\nconst encoded = `KEY=value\n`\n\n// data is Viper's internal representation.\nvar data = map[string]any{\n\t\"KEY\": \"value\",\n}\n\nfunc TestCodec_Encode(t *testing.T) {\n\tcodec := Codec{}\n\n\tb, err := codec.Encode(data)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, encoded, string(b))\n}\n\nfunc TestCodec_Decode(t *testing.T) {\n\tt.Run(\"OK\", func(t *testing.T) {\n\t\tcodec := Codec{}\n\n\t\tv := map[string]any{}\n\n\t\terr := codec.Decode([]byte(original), v)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, data, v)\n\t})\n\n\tt.Run(\"InvalidData\", func(t *testing.T) {\n\t\tcodec := Codec{}\n\n\t\tv := map[string]any{}\n\n\t\terr := codec.Decode([]byte(`invalid data`), v)\n\t\trequire.Error(t, err)\n\n\t\tt.Logf(\"decoding failed as expected: %s\", err)\n\t})\n}\n"
  },
  {
    "path": "internal/encoding/dotenv/map_utils.go",
    "content": "package dotenv\n\nimport (\n\t\"strings\"\n\n\t\"github.com/spf13/cast\"\n)\n\n// flattenAndMergeMap recursively flattens the given map into a new map\n// Code is based on the function with the same name in the main package.\n// TODO: move it to a common place.\nfunc flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any {\n\tif shadow != nil && prefix != \"\" && shadow[prefix] != nil {\n\t\t// prefix is shadowed => nothing more to flatten\n\t\treturn shadow\n\t}\n\tif shadow == nil {\n\t\tshadow = make(map[string]any)\n\t}\n\n\tvar m2 map[string]any\n\tif prefix != \"\" {\n\t\tprefix += delimiter\n\t}\n\tfor k, val := range m {\n\t\tfullKey := prefix + k\n\t\tswitch val := val.(type) {\n\t\tcase map[string]any:\n\t\t\tm2 = val\n\t\tcase map[any]any:\n\t\t\tm2 = cast.ToStringMap(val)\n\t\tdefault:\n\t\t\t// immediate value\n\t\t\tshadow[strings.ToLower(fullKey)] = val\n\t\t\tcontinue\n\t\t}\n\t\t// recursively merge to shadow map\n\t\tshadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter)\n\t}\n\treturn shadow\n}\n"
  },
  {
    "path": "internal/encoding/json/codec.go",
    "content": "package json\n\nimport (\n\t\"encoding/json\"\n)\n\n// Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding.\ntype Codec struct{}\n\n// Encode encodes a map[string]any into a JSON byte slice.\nfunc (Codec) Encode(v map[string]any) ([]byte, error) {\n\t// TODO: expose prefix and indent in the Codec as setting?\n\treturn json.MarshalIndent(v, \"\", \"  \")\n}\n\n// Decode decodes a JSON byte slice into a map[string]any.\nfunc (Codec) Decode(b []byte, v map[string]any) error {\n\treturn json.Unmarshal(b, &v)\n}\n"
  },
  {
    "path": "internal/encoding/json/codec_test.go",
    "content": "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// encoded form of the data.\nconst encoded = `{\n  \"key\": \"value\",\n  \"list\": [\n    \"item1\",\n    \"item2\",\n    \"item3\"\n  ],\n  \"map\": {\n    \"key\": \"value\"\n  },\n  \"nested_map\": {\n    \"map\": {\n      \"key\": \"value\",\n      \"list\": [\n        \"item1\",\n        \"item2\",\n        \"item3\"\n      ]\n    }\n  }\n}`\n\n// data is Viper's internal representation.\nvar data = map[string]any{\n\t\"key\": \"value\",\n\t\"list\": []any{\n\t\t\"item1\",\n\t\t\"item2\",\n\t\t\"item3\",\n\t},\n\t\"map\": map[string]any{\n\t\t\"key\": \"value\",\n\t},\n\t\"nested_map\": map[string]any{\n\t\t\"map\": map[string]any{\n\t\t\t\"key\": \"value\",\n\t\t\t\"list\": []any{\n\t\t\t\t\"item1\",\n\t\t\t\t\"item2\",\n\t\t\t\t\"item3\",\n\t\t\t},\n\t\t},\n\t},\n}\n\nfunc TestCodec_Encode(t *testing.T) {\n\tcodec := Codec{}\n\n\tb, err := codec.Encode(data)\n\trequire.NoError(t, err)\n\n\tassert.JSONEq(t, encoded, string(b))\n}\n\nfunc TestCodec_Decode(t *testing.T) {\n\tt.Run(\"OK\", func(t *testing.T) {\n\t\tcodec := Codec{}\n\n\t\tv := map[string]any{}\n\n\t\terr := codec.Decode([]byte(encoded), v)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, data, v)\n\t})\n\n\tt.Run(\"InvalidData\", func(t *testing.T) {\n\t\tcodec := Codec{}\n\n\t\tv := map[string]any{}\n\n\t\terr := codec.Decode([]byte(`invalid data`), v)\n\t\trequire.Error(t, err)\n\n\t\tt.Logf(\"decoding failed as expected: %s\", err)\n\t})\n}\n"
  },
  {
    "path": "internal/encoding/toml/codec.go",
    "content": "package toml\n\nimport (\n\t\"github.com/pelletier/go-toml/v2\"\n)\n\n// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.\ntype Codec struct{}\n\n// Encode encodes a map[string]any into a TOML byte slice.\nfunc (Codec) Encode(v map[string]any) ([]byte, error) {\n\treturn toml.Marshal(v)\n}\n\n// Decode decodes a TOML byte slice into a map[string]any.\nfunc (Codec) Decode(b []byte, v map[string]any) error {\n\treturn toml.Unmarshal(b, &v)\n}\n"
  },
  {
    "path": "internal/encoding/toml/codec_test.go",
    "content": "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// original form of the data.\nconst original = `# key-value pair\nkey = \"value\"\nlist = [\"item1\", \"item2\", \"item3\"]\n\n[map]\nkey = \"value\"\n\n# nested\n# map\n[nested_map]\n[nested_map.map]\nkey = \"value\"\nlist = [\n  \"item1\",\n  \"item2\",\n  \"item3\",\n]\n`\n\n// encoded form of the data.\nconst encoded = `key = 'value'\nlist = ['item1', 'item2', 'item3']\n\n[map]\nkey = 'value'\n\n[nested_map]\n[nested_map.map]\nkey = 'value'\nlist = ['item1', 'item2', 'item3']\n`\n\n// data is Viper's internal representation.\nvar data = map[string]any{\n\t\"key\": \"value\",\n\t\"list\": []any{\n\t\t\"item1\",\n\t\t\"item2\",\n\t\t\"item3\",\n\t},\n\t\"map\": map[string]any{\n\t\t\"key\": \"value\",\n\t},\n\t\"nested_map\": map[string]any{\n\t\t\"map\": map[string]any{\n\t\t\t\"key\": \"value\",\n\t\t\t\"list\": []any{\n\t\t\t\t\"item1\",\n\t\t\t\t\"item2\",\n\t\t\t\t\"item3\",\n\t\t\t},\n\t\t},\n\t},\n}\n\nfunc TestCodec_Encode(t *testing.T) {\n\tcodec := Codec{}\n\n\tb, err := codec.Encode(data)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, encoded, string(b))\n}\n\nfunc TestCodec_Decode(t *testing.T) {\n\tt.Run(\"OK\", func(t *testing.T) {\n\t\tcodec := Codec{}\n\n\t\tv := map[string]any{}\n\n\t\terr := codec.Decode([]byte(original), v)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, data, v)\n\t})\n\n\tt.Run(\"InvalidData\", func(t *testing.T) {\n\t\tcodec := Codec{}\n\n\t\tv := map[string]any{}\n\n\t\terr := codec.Decode([]byte(`invalid data`), v)\n\t\trequire.Error(t, err)\n\n\t\tt.Logf(\"decoding failed as expected: %s\", err)\n\t})\n}\n"
  },
  {
    "path": "internal/encoding/yaml/codec.go",
    "content": "package yaml\n\nimport \"go.yaml.in/yaml/v3\"\n\n// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.\ntype Codec struct{}\n\n// Encode encodes a map[string]any into a YAML byte slice.\nfunc (Codec) Encode(v map[string]any) ([]byte, error) {\n\treturn yaml.Marshal(v)\n}\n\n// Decode decodes a YAML byte slice into a map[string]any.\nfunc (Codec) Decode(b []byte, v map[string]any) error {\n\treturn yaml.Unmarshal(b, &v)\n}\n"
  },
  {
    "path": "internal/encoding/yaml/codec_test.go",
    "content": "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// original form of the data.\nconst original = `# key-value pair\nkey: value\nlist:\n    - item1\n    - item2\n    - item3\nmap:\n    key: value\n\n# nested\n# map\nnested_map:\n    map:\n        key: value\n        list:\n            - item1\n            - item2\n            - item3\n`\n\n// encoded form of the data.\nconst encoded = `key: value\nlist:\n    - item1\n    - item2\n    - item3\nmap:\n    key: value\nnested_map:\n    map:\n        key: value\n        list:\n            - item1\n            - item2\n            - item3\n`\n\n// decoded form of the data.\n//\n// In case of YAML it's slightly different from Viper's internal representation\n// (e.g. map is decoded into a map with interface key).\nvar decoded = map[string]any{\n\t\"key\": \"value\",\n\t\"list\": []any{\n\t\t\"item1\",\n\t\t\"item2\",\n\t\t\"item3\",\n\t},\n\t\"map\": map[string]any{\n\t\t\"key\": \"value\",\n\t},\n\t\"nested_map\": map[string]any{\n\t\t\"map\": map[string]any{\n\t\t\t\"key\": \"value\",\n\t\t\t\"list\": []any{\n\t\t\t\t\"item1\",\n\t\t\t\t\"item2\",\n\t\t\t\t\"item3\",\n\t\t\t},\n\t\t},\n\t},\n}\n\n// data is Viper's internal representation.\nvar data = map[string]any{\n\t\"key\": \"value\",\n\t\"list\": []any{\n\t\t\"item1\",\n\t\t\"item2\",\n\t\t\"item3\",\n\t},\n\t\"map\": map[string]any{\n\t\t\"key\": \"value\",\n\t},\n\t\"nested_map\": map[string]any{\n\t\t\"map\": map[string]any{\n\t\t\t\"key\": \"value\",\n\t\t\t\"list\": []any{\n\t\t\t\t\"item1\",\n\t\t\t\t\"item2\",\n\t\t\t\t\"item3\",\n\t\t\t},\n\t\t},\n\t},\n}\n\nfunc TestCodec_Encode(t *testing.T) {\n\tcodec := Codec{}\n\n\tb, err := codec.Encode(data)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, encoded, string(b))\n}\n\nfunc TestCodec_Decode(t *testing.T) {\n\tt.Run(\"OK\", func(t *testing.T) {\n\t\tcodec := Codec{}\n\n\t\tv := map[string]any{}\n\n\t\terr := codec.Decode([]byte(original), v)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, decoded, v)\n\t})\n\n\tt.Run(\"InvalidData\", func(t *testing.T) {\n\t\tcodec := Codec{}\n\n\t\tv := map[string]any{}\n\n\t\terr := codec.Decode([]byte(`invalid data`), v)\n\t\trequire.Error(t, err)\n\n\t\tt.Logf(\"decoding failed as expected: %s\", err)\n\t})\n}\n"
  },
  {
    "path": "internal/features/bind_struct.go",
    "content": "//go:build viper_bind_struct\n\npackage features\n\n// BindStruct is a feature flag for enabling/disabling the config binding to structs.\nconst BindStruct = true\n"
  },
  {
    "path": "internal/features/bind_struct_default.go",
    "content": "//go:build !viper_bind_struct\n\npackage features\n\n// BindStruct is a feature flag for enabling/disabling the config binding to structs.\nconst BindStruct = false\n"
  },
  {
    "path": "internal/features/finder.go",
    "content": "//go:build viper_finder\n\npackage features\n\n// Finder is a feature flag for enabling/disabling the config finder.\nconst Finder = true\n"
  },
  {
    "path": "internal/features/finder_default.go",
    "content": "//go:build !viper_finder\n\npackage features\n\n// Finder is a feature flag for enabling/disabling the config finder.\nconst Finder = false\n"
  },
  {
    "path": "internal/testutil/filepath.go",
    "content": "package testutil\n\nimport (\n\t\"path/filepath\"\n\t\"testing\"\n)\n\n// AbsFilePath calls filepath.Abs on path.\nfunc AbsFilePath(t *testing.T, path string) string {\n\tt.Helper()\n\n\ts, err := filepath.Abs(path)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\treturn s\n}\n"
  },
  {
    "path": "logger.go",
    "content": "package viper\n\nimport (\n\t\"context\"\n\t\"log/slog\"\n)\n\n// WithLogger sets a custom logger.\nfunc WithLogger(l *slog.Logger) Option {\n\treturn optionFunc(func(v *Viper) {\n\t\tv.logger = l\n\t})\n}\n\ntype discardHandler struct{}\n\nfunc (n *discardHandler) Enabled(_ context.Context, _ slog.Level) bool {\n\treturn false\n}\n\nfunc (n *discardHandler) Handle(_ context.Context, _ slog.Record) error {\n\treturn nil\n}\n\nfunc (n *discardHandler) WithAttrs(_ []slog.Attr) slog.Handler {\n\treturn n\n}\n\nfunc (n *discardHandler) WithGroup(_ string) slog.Handler {\n\treturn n\n}\n"
  },
  {
    "path": "overrides_test.go",
    "content": "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 layer int\n\nconst (\n\tdefaultLayer layer = iota + 1\n\toverrideLayer\n)\n\nfunc TestNestedOverrides(t *testing.T) {\n\tassert := assert.New(t)\n\tvar v *Viper\n\n\t// Case 0: value overridden by a value\n\toverrideDefault(assert, \"tom\", 10, \"tom\", 20) // \"tom\" is first given 10 as default value, then overridden by 20\n\toverride(assert, \"tom\", 10, \"tom\", 20)        // \"tom\" is first given value 10, then overridden by 20\n\toverrideDefault(assert, \"tom.age\", 10, \"tom.age\", 20)\n\toverride(assert, \"tom.age\", 10, \"tom.age\", 20)\n\toverrideDefault(assert, \"sawyer.tom.age\", 10, \"sawyer.tom.age\", 20)\n\toverride(assert, \"sawyer.tom.age\", 10, \"sawyer.tom.age\", 20)\n\n\t// Case 1: key:value overridden by a value\n\tv = overrideDefault(assert, \"tom.age\", 10, \"tom\", \"boy\") // \"tom.age\" is first given 10 as default value, then \"tom\" is overridden by \"boy\"\n\tassert.Nil(v.Get(\"tom.age\"))                             // \"tom.age\" should not exist anymore\n\tv = override(assert, \"tom.age\", 10, \"tom\", \"boy\")\n\tassert.Nil(v.Get(\"tom.age\"))\n\n\t// Case 2: value overridden by a key:value\n\toverrideDefault(assert, \"tom\", \"boy\", \"tom.age\", 10) // \"tom\" is first given \"boy\" as default value, then \"tom\" is overridden by map{\"age\":10}\n\toverride(assert, \"tom.age\", 10, \"tom\", \"boy\")\n\n\t// Case 3: key:value overridden by a key:value\n\tv = overrideDefault(assert, \"tom.size\", 4, \"tom.age\", 10)\n\tassert.Equal(4, v.Get(\"tom.size\")) // value should still be reachable\n\tv = override(assert, \"tom.size\", 4, \"tom.age\", 10)\n\tassert.Equal(4, v.Get(\"tom.size\"))\n\tdeepCheckValue(assert, v, overrideLayer, []string{\"tom\", \"size\"}, 4)\n\n\t// Case 4: key:value overridden by a map\n\tv = 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}\n\tassert.Equal(4, v.Get(\"tom.size\"))                                           // \"tom.size\" should still be reachable\n\tassert.Equal(10, v.Get(\"tom.age\"))                                           // new value should be there\n\tdeepCheckValue(assert, v, overrideLayer, []string{\"tom\", \"age\"}, 10)         // new value should be there\n\tv = override(assert, \"tom.size\", 4, \"tom\", map[string]any{\"age\": 10})\n\tassert.Nil(v.Get(\"tom.size\"))\n\tassert.Equal(10, v.Get(\"tom.age\"))\n\tdeepCheckValue(assert, v, overrideLayer, []string{\"tom\", \"age\"}, 10)\n\n\t// Case 5: array overridden by a value\n\toverrideDefault(assert, \"tom\", []int{10, 20}, \"tom\", 30)\n\toverride(assert, \"tom\", []int{10, 20}, \"tom\", 30)\n\toverrideDefault(assert, \"tom.age\", []int{10, 20}, \"tom.age\", 30)\n\toverride(assert, \"tom.age\", []int{10, 20}, \"tom.age\", 30)\n\n\t// Case 6: array overridden by an array\n\toverrideDefault(assert, \"tom\", []int{10, 20}, \"tom\", []int{30, 40})\n\toverride(assert, \"tom\", []int{10, 20}, \"tom\", []int{30, 40})\n\toverrideDefault(assert, \"tom.age\", []int{10, 20}, \"tom.age\", []int{30, 40})\n\tv = override(assert, \"tom.age\", []int{10, 20}, \"tom.age\", []int{30, 40})\n\t// explicit array merge:\n\ts, ok := v.Get(\"tom.age\").([]int)\n\tif assert.True(ok, \"tom[\\\"age\\\"] is not a slice\") {\n\t\tv.Set(\"tom.age\", append(s, []int{50, 60}...))\n\t\tassert.Equal([]int{30, 40, 50, 60}, v.Get(\"tom.age\"))\n\t\tdeepCheckValue(assert, v, overrideLayer, []string{\"tom\", \"age\"}, []int{30, 40, 50, 60})\n\t}\n}\n\nfunc overrideDefault(assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {\n\treturn overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue)\n}\n\nfunc override(assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {\n\treturn overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue)\n}\n\n// overrideFromLayer performs the sequential override and low-level checks.\n//\n// First assignment is made on layer l for path firstPath with value firstValue,\n// the second one on the override layer (i.e., with the Set() function)\n// for path secondPath with value secondValue.\n//\n// firstPath and secondPath can include an arbitrary number of dots to indicate\n// a nested element.\n//\n// After each assignment, the value is checked, retrieved both by its full path\n// and by its key sequence (successive maps).\nfunc overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {\n\tv := New()\n\tfirstKeys := strings.Split(firstPath, v.keyDelim)\n\tif assert == nil ||\n\t\tlen(firstKeys) == 0 || firstKeys[0] == \"\" {\n\t\treturn v\n\t}\n\n\t// Set and check first value\n\tswitch l {\n\tcase defaultLayer:\n\t\tv.SetDefault(firstPath, firstValue)\n\tcase overrideLayer:\n\t\tv.Set(firstPath, firstValue)\n\tdefault:\n\t\treturn v\n\t}\n\tassert.Equal(firstValue, v.Get(firstPath))\n\tdeepCheckValue(assert, v, l, firstKeys, firstValue)\n\n\t// Override and check new value\n\tsecondKeys := strings.Split(secondPath, v.keyDelim)\n\tif len(secondKeys) == 0 || secondKeys[0] == \"\" {\n\t\treturn v\n\t}\n\tv.Set(secondPath, secondValue)\n\tassert.Equal(secondValue, v.Get(secondPath))\n\tdeepCheckValue(assert, v, overrideLayer, secondKeys, secondValue)\n\n\treturn v\n}\n\n// deepCheckValue checks that all given keys correspond to a valid path in the\n// configuration map of the given layer, and that the final value equals the one given.\nfunc deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string, value any) {\n\tif assert == nil || v == nil ||\n\t\tlen(keys) == 0 || keys[0] == \"\" {\n\t\treturn\n\t}\n\n\t// init\n\tvar val any\n\tvar ms string\n\tswitch l {\n\tcase defaultLayer:\n\t\tval = v.defaults\n\t\tms = \"v.defaults\"\n\tcase overrideLayer:\n\t\tval = v.override\n\t\tms = \"v.override\"\n\t}\n\n\t// loop through map\n\tvar m map[string]any\n\tfor _, k := range keys {\n\t\tif val == nil {\n\t\t\tassert.Failf(\"%s is not a map[string]any\", ms)\n\t\t\treturn\n\t\t}\n\n\t\t// deep scan of the map to get the final value\n\t\tswitch val := val.(type) {\n\t\tcase map[any]any:\n\t\t\tm = cast.ToStringMap(val)\n\t\tcase map[string]any:\n\t\t\tm = val\n\t\tdefault:\n\t\t\tassert.Failf(\"%s is not a map[string]any\", ms)\n\t\t\treturn\n\t\t}\n\t\tms = ms + \"[\\\"\" + k + \"\\\"]\"\n\t\tval = m[k]\n\t}\n\tassert.Equal(value, val)\n}\n"
  },
  {
    "path": "remote/go.mod",
    "content": "module github.com/spf13/viper/remote\n\ngo 1.23.8\n\nreplace github.com/spf13/viper => ../\n\nrequire (\n\tgithub.com/sagikazarmark/crypt v0.31.0\n\tgithub.com/spf13/viper v1.21.0\n)\n\nrequire (\n\tcloud.google.com/go v0.120.0 // indirect\n\tcloud.google.com/go/auth v0.16.5 // indirect\n\tcloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect\n\tcloud.google.com/go/compute/metadata v0.8.0 // indirect\n\tcloud.google.com/go/firestore v1.18.0 // indirect\n\tcloud.google.com/go/longrunning v0.6.7 // indirect\n\tgithub.com/armon/go-metrics v0.4.1 // indirect\n\tgithub.com/coreos/go-semver v0.3.1 // indirect\n\tgithub.com/coreos/go-systemd/v22 v22.5.0 // indirect\n\tgithub.com/fatih/color v1.16.0 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/fsnotify/fsnotify v1.9.0 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-viper/mapstructure/v2 v2.4.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/google/s2a-go v0.1.9 // indirect\n\tgithub.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect\n\tgithub.com/googleapis/gax-go/v2 v2.15.0 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect\n\tgithub.com/hashicorp/consul/api v1.32.1 // indirect\n\tgithub.com/hashicorp/errwrap v1.1.0 // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/hashicorp/go-hclog v1.5.0 // indirect\n\tgithub.com/hashicorp/go-immutable-radix v1.3.1 // indirect\n\tgithub.com/hashicorp/go-multierror v1.1.1 // indirect\n\tgithub.com/hashicorp/go-rootcerts v1.0.2 // indirect\n\tgithub.com/hashicorp/golang-lru v0.5.4 // indirect\n\tgithub.com/hashicorp/serf v0.10.1 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/klauspost/compress v1.18.0 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.0 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/nats-io/nats.go v1.45.0 // indirect\n\tgithub.com/nats-io/nkeys v0.4.11 // indirect\n\tgithub.com/nats-io/nuid v1.0.1 // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.2.4 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/sagikazarmark/locafero v0.11.0 // indirect\n\tgithub.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect\n\tgithub.com/spf13/afero v1.15.0 // indirect\n\tgithub.com/spf13/cast v1.10.0 // indirect\n\tgithub.com/spf13/pflag v1.0.10 // indirect\n\tgithub.com/subosito/gotenv v1.6.0 // indirect\n\tgo.etcd.io/etcd/api/v3 v3.6.4 // indirect\n\tgo.etcd.io/etcd/client/pkg/v3 v3.6.4 // indirect\n\tgo.etcd.io/etcd/client/v2 v2.305.22 // indirect\n\tgo.etcd.io/etcd/client/v3 v3.6.4 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.1.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect\n\tgo.opentelemetry.io/otel v1.37.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.37.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.37.0 // indirect\n\tgo.uber.org/multierr v1.11.0 // indirect\n\tgo.uber.org/zap v1.27.0 // indirect\n\tgo.yaml.in/yaml/v3 v3.0.4 // indirect\n\tgolang.org/x/crypto v0.41.0 // indirect\n\tgolang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect\n\tgolang.org/x/net v0.43.0 // indirect\n\tgolang.org/x/oauth2 v0.30.0 // indirect\n\tgolang.org/x/sync v0.16.0 // indirect\n\tgolang.org/x/sys v0.35.0 // indirect\n\tgolang.org/x/text v0.28.0 // indirect\n\tgolang.org/x/time v0.12.0 // indirect\n\tgoogle.golang.org/api v0.248.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect\n\tgoogle.golang.org/grpc v1.75.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.7 // indirect\n)\n"
  },
  {
    "path": "remote/go.sum",
    "content": "cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=\ncloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=\ncloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=\ncloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=\ncloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=\ncloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=\ncloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=\ncloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=\ncloud.google.com/go/firestore v1.18.0 h1:cuydCaLS7Vl2SatAeivXyhbhDEIR8BDmtn4egDhIn2s=\ncloud.google.com/go/firestore v1.18.0/go.mod h1:5ye0v48PhseZBdcl0qbl3uttu7FIEwEYVaWm0UIEOEU=\ncloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=\ncloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=\ngithub.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=\ngithub.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=\ngithub.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=\ngithub.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=\ngithub.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=\ngithub.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=\ngithub.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=\ngithub.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=\ngithub.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=\ngithub.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=\ngithub.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=\ngithub.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=\ngithub.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=\ngithub.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=\ngithub.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=\ngithub.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=\ngithub.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE=\ngithub.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4=\ngithub.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg=\ngithub.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=\ngithub.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=\ngithub.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=\ngithub.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=\ngithub.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=\ngithub.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=\ngithub.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=\ngithub.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=\ngithub.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=\ngithub.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=\ngithub.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=\ngithub.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=\ngithub.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=\ngithub.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=\ngithub.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=\ngithub.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=\ngithub.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=\ngithub.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=\ngithub.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=\ngithub.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=\ngithub.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=\ngithub.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=\ngithub.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/nats-io/nats.go v1.45.0 h1:/wGPbnYXDM0pLKFjZTX+2JOw9TQPoIgTFrUaH97giwA=\ngithub.com/nats-io/nats.go v1.45.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=\ngithub.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=\ngithub.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=\ngithub.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=\ngithub.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=\ngithub.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=\ngithub.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=\ngithub.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sagikazarmark/crypt v0.31.0 h1:JJLrH7UojwA5KBkWuuk9x6UgHMzBaU2J2RHpEzUlpAc=\ngithub.com/sagikazarmark/crypt v0.31.0/go.mod h1:X8SJJi7WiZU/Rgdr//EtoELirhl3vah7L7/fcBsO5Hk=\ngithub.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=\ngithub.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=\ngithub.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=\ngithub.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=\ngithub.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=\ngithub.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=\ngithub.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=\ngithub.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=\ngithub.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo=\ngo.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk=\ngo.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0=\ngo.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI=\ngo.etcd.io/etcd/client/v2 v2.305.22 h1:FedDsGxor5iE9muhXm1CgE/TiSVOtgyB5+NYCHPzA2Q=\ngo.etcd.io/etcd/client/v2 v2.305.22/go.mod h1:VP7+1hEKyfGPuRdDmtT8GjM2HcVCKVlGmxfv3NwmrII=\ngo.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A=\ngo.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo=\ngo.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=\ngo.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=\ngo.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=\ngo.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=\ngo.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=\ngo.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=\ngo.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=\ngo.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=\ngo.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=\ngo.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=\ngo.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=\ngo.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=\ngo.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=\ngo.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=\ngolang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=\ngolang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=\ngolang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=\ngolang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=\ngolang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=\ngolang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=\ngolang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=\ngolang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=\ngolang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=\ngolang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=\ngolang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=\ngolang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=\ngonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=\ngoogle.golang.org/api v0.248.0 h1:hUotakSkcwGdYUqzCRc5yGYsg4wXxpkKlW5ryVqvC1Y=\ngoogle.golang.org/api v0.248.0/go.mod h1:yAFUAF56Li7IuIQbTFoLwXTCI6XCFKueOlS7S9e4F9k=\ngoogle.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=\ngoogle.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=\ngoogle.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=\ngoogle.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=\ngoogle.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=\ngoogle.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "remote/remote.go",
    "content": "// Copyright © 2015 Steve Francia <spf@spf13.com>.\n//\n// Use of this source code is governed by an MIT-style\n// license that can be found in the LICENSE file.\n\n// Package remote integrates the remote features of Viper.\npackage remote\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\tcrypt \"github.com/sagikazarmark/crypt/config\"\n\n\t\"github.com/spf13/viper\"\n)\n\ntype remoteConfigProvider struct{}\n\nfunc (rc remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) {\n\tcm, err := getConfigManager(rp)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tb, err := cm.Get(rp.Path())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn bytes.NewReader(b), nil\n}\n\nfunc (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) {\n\tcm, err := getConfigManager(rp)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresp, err := cm.Get(rp.Path())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn bytes.NewReader(resp), nil\n}\n\nfunc (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {\n\tcm, err := getConfigManager(rp)\n\tif err != nil {\n\t\treturn nil, nil\n\t}\n\tquit := make(chan bool)\n\tquitwc := make(chan bool)\n\tviperResponsCh := make(chan *viper.RemoteResponse)\n\tcryptoResponseCh := cm.Watch(rp.Path(), quit)\n\t// need this function to convert the Channel response form crypt.Response to viper.Response\n\tgo func(cr <-chan *crypt.Response, vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-quitwc:\n\t\t\t\tquit <- true\n\t\t\t\treturn\n\t\t\tcase resp := <-cr:\n\t\t\t\tvr <- &viper.RemoteResponse{\n\t\t\t\t\tError: resp.Error,\n\t\t\t\t\tValue: resp.Value,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}(cryptoResponseCh, viperResponsCh, quitwc, quit)\n\n\treturn viperResponsCh, quitwc\n}\n\nfunc getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {\n\tvar cm crypt.ConfigManager\n\tvar err error\n\n\tendpoints := strings.Split(rp.Endpoint(), \";\")\n\tif rp.SecretKeyring() != \"\" {\n\t\tvar kr *os.File\n\t\tkr, err = os.Open(rp.SecretKeyring())\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer kr.Close()\n\t\tswitch rp.Provider() {\n\t\tcase \"etcd\":\n\t\t\tcm, err = crypt.NewEtcdConfigManager(endpoints, kr)\n\t\tcase \"etcd3\":\n\t\t\tcm, err = crypt.NewEtcdV3ConfigManager(endpoints, kr)\n\t\tcase \"firestore\":\n\t\t\tcm, err = crypt.NewFirestoreConfigManager(endpoints, kr)\n\t\tcase \"nats\":\n\t\t\tcm, err = crypt.NewNatsConfigManager(endpoints, kr)\n\t\tdefault:\n\t\t\tcm, err = crypt.NewConsulConfigManager(endpoints, kr)\n\t\t}\n\t} else {\n\t\tswitch rp.Provider() {\n\t\tcase \"etcd\":\n\t\t\tcm, err = crypt.NewStandardEtcdConfigManager(endpoints)\n\t\tcase \"etcd3\":\n\t\t\tcm, err = crypt.NewStandardEtcdV3ConfigManager(endpoints)\n\t\tcase \"firestore\":\n\t\t\tcm, err = crypt.NewStandardFirestoreConfigManager(endpoints)\n\t\tcase \"nats\":\n\t\t\tcm, err = crypt.NewStandardNatsConfigManager(endpoints)\n\t\tdefault:\n\t\t\tcm, err = crypt.NewStandardConsulConfigManager(endpoints)\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn cm, nil\n}\n\nfunc init() {\n\tviper.RemoteConfig = &remoteConfigProvider{}\n}\n"
  },
  {
    "path": "remote.go",
    "content": "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 supported remote providers.\nvar SupportedRemoteProviders = []string{\"etcd\", \"etcd3\", \"consul\", \"firestore\", \"nats\"}\n\nfunc resetRemote() {\n\tSupportedRemoteProviders = []string{\"etcd\", \"etcd3\", \"consul\", \"firestore\", \"nats\"}\n}\n\ntype remoteConfigFactory interface {\n\tGet(rp RemoteProvider) (io.Reader, error)\n\tWatch(rp RemoteProvider) (io.Reader, error)\n\tWatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool)\n}\n\n// RemoteResponse represents a response from a remote configuration provider.\ntype RemoteResponse struct {\n\tValue []byte\n\tError error\n}\n\n// RemoteConfig is optional, see the remote package.\nvar RemoteConfig remoteConfigFactory\n\n// UnsupportedRemoteProviderError denotes encountering an unsupported remote\n// provider. Currently only etcd and Consul are supported.\ntype UnsupportedRemoteProviderError string\n\n// Error returns the formatted remote provider error.\nfunc (str UnsupportedRemoteProviderError) Error() string {\n\treturn fmt.Sprintf(\"Unsupported Remote Provider Type %q\", string(str))\n}\n\n// RemoteConfigError denotes encountering an error while trying to\n// pull the configuration from the remote provider.\ntype RemoteConfigError string\n\n// Error returns the formatted remote provider error.\nfunc (rce RemoteConfigError) Error() string {\n\treturn fmt.Sprintf(\"Remote Configurations Error: %s\", string(rce))\n}\n\ntype defaultRemoteProvider struct {\n\tprovider      string\n\tendpoint      string\n\tpath          string\n\tsecretKeyring string\n}\n\nfunc (rp defaultRemoteProvider) Provider() string {\n\treturn rp.provider\n}\n\nfunc (rp defaultRemoteProvider) Endpoint() string {\n\treturn rp.endpoint\n}\n\nfunc (rp defaultRemoteProvider) Path() string {\n\treturn rp.path\n}\n\nfunc (rp defaultRemoteProvider) SecretKeyring() string {\n\treturn rp.secretKeyring\n}\n\n// RemoteProvider stores the configuration necessary\n// to connect to a remote key/value store.\n// Optional secretKeyring to unencrypt encrypted values\n// can be provided.\ntype RemoteProvider interface {\n\tProvider() string\n\tEndpoint() string\n\tPath() string\n\tSecretKeyring() string\n}\n\n// AddRemoteProvider adds a remote configuration source.\n// Remote Providers are searched in the order they are added.\n// provider is a string value: \"etcd\", \"etcd3\", \"consul\", \"firestore\" or \"nats\" are currently supported.\n// endpoint is the url.  etcd requires http://ip:port, consul requires ip:port, nats requires nats://ip:port\n// path is the path in the k/v store to retrieve configuration\n// To retrieve a config file called myapp.json from /configs/myapp.json\n// you should set path to /configs and set config name (SetConfigName()) to\n// \"myapp\".\nfunc AddRemoteProvider(provider, endpoint, path string) error {\n\treturn v.AddRemoteProvider(provider, endpoint, path)\n}\n\n// AddRemoteProvider adds a remote configuration source.\n// Remote Providers are searched in the order they are added.\n// provider is a string value: \"etcd\", \"etcd3\", \"consul\", \"firestore\" or \"nats\" are currently supported.\n// endpoint is the url.  etcd requires http://ip:port, consul requires ip:port, nats requires nats://ip:port\n// path is the path in the k/v store to retrieve configuration\n// To retrieve a config file called myapp.json from /configs/myapp.json\n// you should set path to /configs and set config name (SetConfigName()) to\n// \"myapp\".\nfunc (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {\n\tif !slices.Contains(SupportedRemoteProviders, provider) {\n\t\treturn UnsupportedRemoteProviderError(provider)\n\t}\n\tif provider != \"\" && endpoint != \"\" {\n\t\tv.logger.Info(\"adding remote provider\", \"provider\", provider, \"endpoint\", endpoint)\n\n\t\trp := &defaultRemoteProvider{\n\t\t\tendpoint: endpoint,\n\t\t\tprovider: provider,\n\t\t\tpath:     path,\n\t\t}\n\t\tif !v.providerPathExists(rp) {\n\t\t\tv.remoteProviders = append(v.remoteProviders, rp)\n\t\t}\n\t}\n\treturn nil\n}\n\n// AddSecureRemoteProvider adds a remote configuration source.\n// Secure Remote Providers are searched in the order they are added.\n// provider is a string value: \"etcd\", \"etcd3\", \"consul\", \"firestore\" or \"nats\" are currently supported.\n// endpoint is the url.  etcd requires http://ip:port  consul requires ip:port\n// secretkeyring is the filepath to your openpgp secret keyring.  e.g. /etc/secrets/myring.gpg\n// path is the path in the k/v store to retrieve configuration\n// To retrieve a config file called myapp.json from /configs/myapp.json\n// you should set path to /configs and set config name (SetConfigName()) to\n// \"myapp\".\n// Secure Remote Providers are implemented with github.com/sagikazarmark/crypt.\nfunc AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {\n\treturn v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring)\n}\n\n// AddSecureRemoteProvider adds a remote configuration source.\n// Secure Remote Providers are searched in the order they are added.\n// provider is a string value: \"etcd\", \"etcd3\", \"consul\", \"firestore\" or \"nats\" are currently supported.\n// endpoint is the url.  etcd requires http://ip:port  consul requires ip:port\n// secretkeyring is the filepath to your openpgp secret keyring.  e.g. /etc/secrets/myring.gpg\n// path is the path in the k/v store to retrieve configuration\n// To retrieve a config file called myapp.json from /configs/myapp.json\n// you should set path to /configs and set config name (SetConfigName()) to\n// \"myapp\".\n// Secure Remote Providers are implemented with github.com/sagikazarmark/crypt.\nfunc (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {\n\tif !slices.Contains(SupportedRemoteProviders, provider) {\n\t\treturn UnsupportedRemoteProviderError(provider)\n\t}\n\tif provider != \"\" && endpoint != \"\" {\n\t\tv.logger.Info(\"adding remote provider\", \"provider\", provider, \"endpoint\", endpoint)\n\n\t\trp := &defaultRemoteProvider{\n\t\t\tendpoint:      endpoint,\n\t\t\tprovider:      provider,\n\t\t\tpath:          path,\n\t\t\tsecretKeyring: secretkeyring,\n\t\t}\n\t\tif !v.providerPathExists(rp) {\n\t\t\tv.remoteProviders = append(v.remoteProviders, rp)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {\n\tfor _, y := range v.remoteProviders {\n\t\tif reflect.DeepEqual(y, p) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// ReadRemoteConfig attempts to get configuration from a remote source\n// and read it in the remote configuration registry.\nfunc ReadRemoteConfig() error { return v.ReadRemoteConfig() }\n\n// ReadRemoteConfig attempts to get configuration from a remote source\n// and read it in the remote configuration registry.\nfunc (v *Viper) ReadRemoteConfig() error {\n\treturn v.getKeyValueConfig()\n}\n\n// WatchRemoteConfig updates configuration from available remote providers.\nfunc WatchRemoteConfig() error { return v.WatchRemoteConfig() }\n\n// WatchRemoteConfig updates configuration from available remote providers.\nfunc (v *Viper) WatchRemoteConfig() error {\n\treturn v.watchKeyValueConfig()\n}\n\n// WatchRemoteConfigOnChannel updates configuration from available remote providers.\nfunc (v *Viper) WatchRemoteConfigOnChannel() error {\n\treturn v.watchKeyValueConfigOnChannel()\n}\n\n// Retrieve the first found remote configuration.\nfunc (v *Viper) getKeyValueConfig() error {\n\tif RemoteConfig == nil {\n\t\treturn RemoteConfigError(\"Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'\")\n\t}\n\n\tif len(v.remoteProviders) == 0 {\n\t\treturn RemoteConfigError(\"No Remote Providers\")\n\t}\n\n\tfor _, rp := range v.remoteProviders {\n\t\tval, err := v.getRemoteConfig(rp)\n\t\tif err != nil {\n\t\t\tv.logger.Error(fmt.Errorf(\"get remote config: %w\", err).Error())\n\n\t\t\tcontinue\n\t\t}\n\n\t\tv.kvstore = val\n\n\t\treturn nil\n\t}\n\treturn RemoteConfigError(\"No Files Found\")\n}\n\nfunc (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]any, error) {\n\treader, err := RemoteConfig.Get(provider)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = v.unmarshalReader(reader, v.kvstore)\n\treturn v.kvstore, err\n}\n\n// Retrieve the first found remote configuration.\nfunc (v *Viper) watchKeyValueConfigOnChannel() error {\n\tif len(v.remoteProviders) == 0 {\n\t\treturn RemoteConfigError(\"No Remote Providers\")\n\t}\n\n\tfor _, rp := range v.remoteProviders {\n\t\trespc, _ := RemoteConfig.WatchChannel(rp)\n\t\t// Todo: Add quit channel\n\t\tgo func(rc <-chan *RemoteResponse) {\n\t\t\tfor {\n\t\t\t\tb := <-rc\n\t\t\t\treader := bytes.NewReader(b.Value)\n\t\t\t\terr := v.unmarshalReader(reader, v.kvstore)\n\t\t\t\tif err != nil {\n\t\t\t\t\tv.logger.Error(fmt.Errorf(\"failed to unmarshal remote config: %w\", err).Error())\n\t\t\t\t}\n\t\t\t}\n\t\t}(respc)\n\t\treturn nil\n\t}\n\treturn RemoteConfigError(\"No Files Found\")\n}\n\n// Retrieve the first found remote configuration.\nfunc (v *Viper) watchKeyValueConfig() error {\n\tif len(v.remoteProviders) == 0 {\n\t\treturn RemoteConfigError(\"No Remote Providers\")\n\t}\n\n\tfor _, rp := range v.remoteProviders {\n\t\tval, err := v.watchRemoteConfig(rp)\n\t\tif err != nil {\n\t\t\tv.logger.Error(fmt.Errorf(\"watch remote config: %w\", err).Error())\n\n\t\t\tcontinue\n\t\t}\n\t\tv.kvstore = val\n\t\treturn nil\n\t}\n\treturn RemoteConfigError(\"No Files Found\")\n}\n\nfunc (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]any, error) {\n\treader, err := RemoteConfig.Watch(provider)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = v.unmarshalReader(reader, v.kvstore)\n\treturn v.kvstore, err\n}\n"
  },
  {
    "path": "util.go",
    "content": "// Copyright © 2014 Steve Francia <spf@spf13.com>.\n//\n// Use of this source code is governed by an MIT-style\n// license that can be found in the LICENSE file.\n\n// Viper is a application configuration system.\n// It believes that applications can be configured a variety of ways\n// via flags, ENVIRONMENT variables, configuration files retrieved\n// from the file system, or a remote key/value store.\n\npackage viper\n\nimport (\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/spf13/cast\"\n)\n\n// ConfigParseError denotes failing to parse configuration file.\ntype ConfigParseError struct {\n\terr error\n}\n\n// Error returns the formatted configuration error.\nfunc (pe ConfigParseError) Error() string {\n\treturn fmt.Sprintf(\"While parsing config: %s\", pe.err.Error())\n}\n\n// Unwrap returns the wrapped error.\nfunc (pe ConfigParseError) Unwrap() error {\n\treturn pe.err\n}\n\n// toCaseInsensitiveValue checks if the value is a  map;\n// if so, create a copy and lower-case the keys recursively.\nfunc toCaseInsensitiveValue(value any) any {\n\tswitch v := value.(type) {\n\tcase map[any]any:\n\t\tvalue = copyAndInsensitiviseMap(cast.ToStringMap(v))\n\tcase map[string]any:\n\t\tvalue = copyAndInsensitiviseMap(v)\n\t}\n\n\treturn value\n}\n\n// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of\n// any map it makes case insensitive.\nfunc copyAndInsensitiviseMap(m map[string]any) map[string]any {\n\tnm := make(map[string]any)\n\n\tfor key, val := range m {\n\t\tlkey := strings.ToLower(key)\n\t\tswitch v := val.(type) {\n\t\tcase map[any]any:\n\t\t\tnm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))\n\t\tcase map[string]any:\n\t\t\tnm[lkey] = copyAndInsensitiviseMap(v)\n\t\tdefault:\n\t\t\tnm[lkey] = v\n\t\t}\n\t}\n\n\treturn nm\n}\n\nfunc insensitiviseVal(val any) any {\n\tswitch v := val.(type) {\n\tcase map[any]any:\n\t\t// nested map: cast and recursively insensitivise\n\t\tval = cast.ToStringMap(val)\n\t\tinsensitiviseMap(val.(map[string]any))\n\tcase map[string]any:\n\t\t// nested map: recursively insensitivise\n\t\tinsensitiviseMap(v)\n\tcase []any:\n\t\t// nested array: recursively insensitivise\n\t\tinsensitiveArray(v)\n\t}\n\treturn val\n}\n\nfunc insensitiviseMap(m map[string]any) {\n\tfor key, val := range m {\n\t\tval = insensitiviseVal(val)\n\t\tlower := strings.ToLower(key)\n\t\tif key != lower {\n\t\t\t// remove old key (not lower-cased)\n\t\t\tdelete(m, key)\n\t\t}\n\t\t// update map\n\t\tm[lower] = val\n\t}\n}\n\nfunc insensitiveArray(a []any) {\n\tfor i, val := range a {\n\t\ta[i] = insensitiviseVal(val)\n\t}\n}\n\nfunc absPathify(logger *slog.Logger, inPath string) string {\n\tlogger.Info(\"trying to resolve absolute path\", \"path\", inPath)\n\n\tif inPath == \"$HOME\" || strings.HasPrefix(inPath, \"$HOME\"+string(os.PathSeparator)) {\n\t\tinPath = userHomeDir() + inPath[5:]\n\t}\n\n\tinPath = os.ExpandEnv(inPath)\n\n\tif filepath.IsAbs(inPath) {\n\t\treturn filepath.Clean(inPath)\n\t}\n\n\tp, err := filepath.Abs(inPath)\n\tif err == nil {\n\t\treturn filepath.Clean(p)\n\t}\n\n\tlogger.Error(fmt.Errorf(\"could not discover absolute path: %w\", err).Error())\n\n\treturn \"\"\n}\n\nfunc userHomeDir() string {\n\tif runtime.GOOS == \"windows\" {\n\t\thome := os.Getenv(\"HOMEDRIVE\") + os.Getenv(\"HOMEPATH\")\n\t\tif home == \"\" {\n\t\t\thome = os.Getenv(\"USERPROFILE\")\n\t\t}\n\t\treturn home\n\t}\n\treturn os.Getenv(\"HOME\")\n}\n\nfunc safeMul(a, b uint) uint {\n\tc := a * b\n\tif a > 1 && b > 1 && c/b != a {\n\t\treturn 0\n\t}\n\treturn c\n}\n\n// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes.\nfunc parseSizeInBytes(sizeStr string) uint {\n\tsizeStr = strings.TrimSpace(sizeStr)\n\tlastChar := len(sizeStr) - 1\n\tmultiplier := uint(1)\n\n\tif lastChar > 0 {\n\t\tif sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {\n\t\t\tif lastChar > 1 {\n\t\t\t\tswitch unicode.ToLower(rune(sizeStr[lastChar-1])) {\n\t\t\t\tcase 'k':\n\t\t\t\t\tmultiplier = 1 << 10\n\t\t\t\t\tsizeStr = strings.TrimSpace(sizeStr[:lastChar-1])\n\t\t\t\tcase 'm':\n\t\t\t\t\tmultiplier = 1 << 20\n\t\t\t\t\tsizeStr = strings.TrimSpace(sizeStr[:lastChar-1])\n\t\t\t\tcase 'g':\n\t\t\t\t\tmultiplier = 1 << 30\n\t\t\t\t\tsizeStr = strings.TrimSpace(sizeStr[:lastChar-1])\n\t\t\t\tdefault:\n\t\t\t\t\tmultiplier = 1\n\t\t\t\t\tsizeStr = strings.TrimSpace(sizeStr[:lastChar])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tsize := max(cast.ToInt(sizeStr), 0)\n\n\treturn safeMul(uint(size), multiplier)\n}\n\n// deepSearch scans deep maps, following the key indexes listed in the\n// sequence \"path\".\n// The last value is expected to be another map, and is returned.\n//\n// In case intermediate keys do not exist, or map to a non-map value,\n// a new map is created and inserted, and the search continues from there:\n// the initial map \"m\" may be modified!\nfunc deepSearch(m map[string]any, path []string) map[string]any {\n\tfor _, k := range path {\n\t\tm2, ok := m[k]\n\t\tif !ok {\n\t\t\t// intermediate key does not exist\n\t\t\t// => create it and continue from there\n\t\t\tm3 := make(map[string]any)\n\t\t\tm[k] = m3\n\t\t\tm = m3\n\t\t\tcontinue\n\t\t}\n\t\tm3, ok := m2.(map[string]any)\n\t\tif !ok {\n\t\t\t// intermediate key is a value\n\t\t\t// => replace with a new map\n\t\t\tm3 = make(map[string]any)\n\t\t\tm[k] = m3\n\t\t}\n\t\t// continue search from here\n\t\tm = m3\n\t}\n\treturn m\n}\n"
  },
  {
    "path": "util_test.go",
    "content": "// Copyright © 2016 Steve Francia <spf@spf13.com>.\n//\n// Use of this source code is governed by an MIT-style\n// license that can be found in the LICENSE file.\n\n// Viper is a application configuration system.\n// It believes that applications can be configured a variety of ways\n// via flags, ENVIRONMENT variables, configuration files retrieved\n// from the file system, or a remote key/value store.\n\npackage viper\n\nimport (\n\t\"log/slog\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCopyAndInsensitiviseMap(t *testing.T) {\n\tvar (\n\t\tgiven = map[string]any{\n\t\t\t\"Foo\": 32,\n\t\t\t\"Bar\": map[any]any{\n\t\t\t\t\"ABc\": \"A\",\n\t\t\t\t\"cDE\": \"B\",\n\t\t\t},\n\t\t}\n\t\texpected = map[string]any{\n\t\t\t\"foo\": 32,\n\t\t\t\"bar\": map[string]any{\n\t\t\t\t\"abc\": \"A\",\n\t\t\t\t\"cde\": \"B\",\n\t\t\t},\n\t\t}\n\t)\n\n\tgot := copyAndInsensitiviseMap(given)\n\n\tassert.Equal(t, expected, got)\n\t_, ok := given[\"foo\"]\n\tassert.False(t, ok)\n\t_, ok = given[\"bar\"]\n\tassert.False(t, ok)\n\n\tm := given[\"Bar\"].(map[any]any)\n\t_, ok = m[\"ABc\"]\n\tassert.True(t, ok)\n}\n\nfunc TestAbsPathify(t *testing.T) {\n\tskipWindows(t)\n\n\thome := userHomeDir()\n\thomer := filepath.Join(home, \"homer\")\n\twd, _ := os.Getwd()\n\n\tt.Setenv(\"HOMER_ABSOLUTE_PATH\", homer)\n\tt.Setenv(\"VAR_WITH_RELATIVE_PATH\", \"relative\")\n\n\ttests := []struct {\n\t\tinput  string\n\t\toutput string\n\t}{\n\t\t{\"\", wd},\n\t\t{\"sub\", filepath.Join(wd, \"sub\")},\n\t\t{\"./\", wd},\n\t\t{\"./sub\", filepath.Join(wd, \"sub\")},\n\t\t{\"$HOME\", home},\n\t\t{\"$HOME/\", home},\n\t\t{\"$HOME/sub\", filepath.Join(home, \"sub\")},\n\t\t{\"$HOMER_ABSOLUTE_PATH\", homer},\n\t\t{\"$HOMER_ABSOLUTE_PATH/\", homer},\n\t\t{\"$HOMER_ABSOLUTE_PATH/sub\", filepath.Join(homer, \"sub\")},\n\t\t{\"$VAR_WITH_RELATIVE_PATH\", filepath.Join(wd, \"relative\")},\n\t\t{\"$VAR_WITH_RELATIVE_PATH/\", filepath.Join(wd, \"relative\")},\n\t\t{\"$VAR_WITH_RELATIVE_PATH/sub\", filepath.Join(wd, \"relative\", \"sub\")},\n\t}\n\n\tfor _, test := range tests {\n\t\tgot := absPathify(slog.Default(), test.input)\n\t\tassert.Equal(t, test.output, got)\n\t}\n}\n"
  },
  {
    "path": "viper.go",
    "content": "// Copyright © 2014 Steve Francia <spf@spf13.com>.\n//\n// Use of this source code is governed by an MIT-style\n// license that can be found in the LICENSE file.\n\n// Viper is an application configuration system.\n// It believes that applications can be configured a variety of ways\n// via flags, ENVIRONMENT variables, configuration files retrieved\n// from the file system, or a remote key/value store.\n\n// Each item takes precedence over the item below it:\n\n// overrides\n// flag\n// env\n// config\n// key/value store\n// default\n\npackage viper\n\nimport (\n\t\"bytes\"\n\t\"encoding/csv\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\tfs \"io/fs\"\n\t\"log/slog\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/go-viper/mapstructure/v2\"\n\t\"github.com/spf13/afero\"\n\t\"github.com/spf13/cast\"\n\t\"github.com/spf13/pflag\"\n\n\t\"github.com/spf13/viper/internal/features\"\n)\n\nvar v *Viper\n\nfunc init() {\n\tv = New()\n}\n\n// A DecoderConfigOption can be passed to viper.Unmarshal to configure\n// mapstructure.DecoderConfig options.\ntype DecoderConfigOption func(*mapstructure.DecoderConfig)\n\n// DecodeHook returns a DecoderConfigOption which overrides the default\n// DecoderConfig.DecodeHook value, the default is:\n//\n//\t mapstructure.ComposeDecodeHookFunc(\n//\t\t\tmapstructure.StringToTimeDurationHookFunc(),\n//\t\t\tmapstructure.StringToSliceHookFunc(\",\"),\n//\t\t)\nfunc DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {\n\treturn func(c *mapstructure.DecoderConfig) {\n\t\tc.DecodeHook = hook\n\t}\n}\n\n// Viper is a prioritized configuration registry. It\n// maintains a set of configuration sources, fetches\n// values to populate those, and provides them according\n// to the source's priority.\n// The priority of the sources is the following:\n// 1. overrides\n// 2. flags\n// 3. env. variables\n// 4. config file\n// 5. key/value store\n// 6. defaults\n//\n// For example, if values from the following sources were loaded:\n//\n//\tDefaults : {\n//\t\t\"secret\": \"\",\n//\t\t\"user\": \"default\",\n//\t\t\"endpoint\": \"https://localhost\"\n//\t}\n//\tConfig : {\n//\t\t\"user\": \"root\"\n//\t\t\"secret\": \"defaultsecret\"\n//\t}\n//\tEnv : {\n//\t\t\"secret\": \"somesecretkey\"\n//\t}\n//\n// The resulting config will have the following values:\n//\n//\t{\n//\t\t\"secret\": \"somesecretkey\",\n//\t\t\"user\": \"root\",\n//\t\t\"endpoint\": \"https://localhost\"\n//\t}\n//\n// Note: Vipers are not safe for concurrent Get() and Set() operations.\ntype Viper struct {\n\t// Delimiter that separates a list of keys\n\t// used to access a nested value in one go\n\tkeyDelim string\n\n\t// A set of paths to look for the config file in\n\tconfigPaths []string\n\n\t// The filesystem to read config from.\n\tfs afero.Fs\n\n\tfinder Finder\n\n\t// A set of remote providers to search for the configuration\n\tremoteProviders []*defaultRemoteProvider\n\n\t// Name of file to look for inside the path\n\tconfigName        string\n\tconfigFile        string\n\tconfigType        string\n\tconfigPermissions os.FileMode\n\tenvPrefix         string\n\n\tautomaticEnvApplied bool\n\tenvKeyReplacer      StringReplacer\n\tallowEmptyEnv       bool\n\n\tparents        []string\n\tconfig         map[string]any\n\toverride       map[string]any\n\tdefaults       map[string]any\n\tkvstore        map[string]any\n\tpflags         map[string]FlagValue\n\tenv            map[string][]string\n\taliases        map[string]string\n\ttypeByDefValue bool\n\n\tonConfigChange func(fsnotify.Event)\n\n\tlogger *slog.Logger\n\n\tencoderRegistry EncoderRegistry\n\tdecoderRegistry DecoderRegistry\n\n\tdecodeHook mapstructure.DecodeHookFunc\n\n\texperimentalFinder     bool\n\texperimentalBindStruct bool\n}\n\n// New returns an initialized Viper instance.\nfunc New() *Viper {\n\tv := new(Viper)\n\tv.keyDelim = \".\"\n\tv.configName = \"config\"\n\tv.configPermissions = os.FileMode(0o644)\n\tv.fs = afero.NewOsFs()\n\tv.config = make(map[string]any)\n\tv.parents = []string{}\n\tv.override = make(map[string]any)\n\tv.defaults = make(map[string]any)\n\tv.kvstore = make(map[string]any)\n\tv.pflags = make(map[string]FlagValue)\n\tv.env = make(map[string][]string)\n\tv.aliases = make(map[string]string)\n\tv.typeByDefValue = false\n\tv.logger = slog.New(&discardHandler{})\n\n\tcodecRegistry := NewCodecRegistry()\n\n\tv.encoderRegistry = codecRegistry\n\tv.decoderRegistry = codecRegistry\n\n\tv.experimentalFinder = features.Finder\n\tv.experimentalBindStruct = features.BindStruct\n\n\treturn v\n}\n\n// Option configures Viper using the functional options paradigm popularized by Rob Pike and Dave Cheney.\n// If you're unfamiliar with this style,\n// see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and\n// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis.\ntype Option interface {\n\tapply(v *Viper)\n}\n\ntype optionFunc func(v *Viper)\n\nfunc (fn optionFunc) apply(v *Viper) {\n\tfn(v)\n}\n\n// KeyDelimiter sets the delimiter used for determining key parts.\n// By default it's value is \".\".\nfunc KeyDelimiter(d string) Option {\n\treturn optionFunc(func(v *Viper) {\n\t\tv.keyDelim = d\n\t})\n}\n\n// StringReplacer applies a set of replacements to a string.\ntype StringReplacer interface {\n\t// Replace returns a copy of s with all replacements performed.\n\tReplace(s string) string\n}\n\n// EnvKeyReplacer sets a replacer used for mapping environment variables to internal keys.\nfunc EnvKeyReplacer(r StringReplacer) Option {\n\treturn optionFunc(func(v *Viper) {\n\t\tif r == nil {\n\t\t\treturn\n\t\t}\n\n\t\tv.envKeyReplacer = r\n\t})\n}\n\n// WithDecodeHook sets a default decode hook for mapstructure.\nfunc WithDecodeHook(h mapstructure.DecodeHookFunc) Option {\n\treturn optionFunc(func(v *Viper) {\n\t\tif h == nil {\n\t\t\treturn\n\t\t}\n\n\t\tv.decodeHook = h\n\t})\n}\n\n// NewWithOptions creates a new Viper instance.\nfunc NewWithOptions(opts ...Option) *Viper {\n\tv := New()\n\n\tfor _, opt := range opts {\n\t\topt.apply(v)\n\t}\n\n\treturn v\n}\n\n// SetOptions sets the options on the global Viper instance.\n//\n// Be careful when using this function: subsequent calls may override options you set.\n// It's always better to use a local Viper instance.\nfunc SetOptions(opts ...Option) {\n\tfor _, opt := range opts {\n\t\topt.apply(v)\n\t}\n}\n\n// Reset is intended for testing, will reset all to default settings.\n// In the public interface for the viper package so applications\n// can use it in their testing as well.\nfunc Reset() {\n\tv = New()\n\tSupportedExts = []string{\"json\", \"toml\", \"yaml\", \"yml\", \"properties\", \"props\", \"prop\", \"hcl\", \"tfvars\", \"dotenv\", \"env\", \"ini\"}\n\n\tresetRemote()\n}\n\n// SupportedExts are universally supported extensions.\nvar SupportedExts = []string{\"json\", \"toml\", \"yaml\", \"yml\", \"properties\", \"props\", \"prop\", \"hcl\", \"tfvars\", \"dotenv\", \"env\", \"ini\"}\n\n// OnConfigChange sets the event handler that is called when a config file changes.\nfunc OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }\n\n// OnConfigChange sets the event handler that is called when a config file changes.\nfunc (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {\n\tv.onConfigChange = run\n}\n\n// WatchConfig starts watching a config file for changes.\nfunc WatchConfig() { v.WatchConfig() }\n\n// WatchConfig starts watching a config file for changes.\nfunc (v *Viper) WatchConfig() {\n\tinitWG := sync.WaitGroup{}\n\tinitWG.Add(1)\n\tgo func() {\n\t\twatcher, err := fsnotify.NewWatcher()\n\t\tif err != nil {\n\t\t\tv.logger.Error(fmt.Sprintf(\"failed to create watcher: %s\", err))\n\t\t\tos.Exit(1)\n\t\t}\n\t\tdefer watcher.Close()\n\t\t// we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way\n\t\tfilename, err := v.getConfigFile()\n\t\tif err != nil {\n\t\t\tv.logger.Error(fmt.Sprintf(\"get config file: %s\", err))\n\t\t\tinitWG.Done()\n\t\t\treturn\n\t\t}\n\n\t\tconfigFile := filepath.Clean(filename)\n\t\tconfigDir, _ := filepath.Split(configFile)\n\t\trealConfigFile, _ := filepath.EvalSymlinks(filename)\n\n\t\teventsWG := sync.WaitGroup{}\n\t\teventsWG.Add(1)\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase event, ok := <-watcher.Events:\n\t\t\t\t\tif !ok { // 'Events' channel is closed\n\t\t\t\t\t\teventsWG.Done()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tcurrentConfigFile, _ := filepath.EvalSymlinks(filename)\n\t\t\t\t\t// we only care about the config file with the following cases:\n\t\t\t\t\t// 1 - if the config file was modified or created\n\t\t\t\t\t// 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement)\n\t\t\t\t\tif (filepath.Clean(event.Name) == configFile &&\n\t\t\t\t\t\t(event.Has(fsnotify.Write) || event.Has(fsnotify.Create))) ||\n\t\t\t\t\t\t(currentConfigFile != \"\" && currentConfigFile != realConfigFile) {\n\t\t\t\t\t\trealConfigFile = currentConfigFile\n\t\t\t\t\t\terr := v.ReadInConfig()\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tv.logger.Error(fmt.Sprintf(\"read config file: %s\", err))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif v.onConfigChange != nil {\n\t\t\t\t\t\t\tv.onConfigChange(event)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if filepath.Clean(event.Name) == configFile && event.Has(fsnotify.Remove) {\n\t\t\t\t\t\teventsWG.Done()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\tcase err, ok := <-watcher.Errors:\n\t\t\t\t\tif ok { // 'Errors' channel is not closed\n\t\t\t\t\t\tv.logger.Error(fmt.Sprintf(\"watcher error: %s\", err))\n\t\t\t\t\t}\n\t\t\t\t\teventsWG.Done()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t\terr = watcher.Add(configDir)\n\t\tif err != nil {\n\t\t\tv.logger.Error(fmt.Sprintf(\"failed to add watcher: %s\", err))\n\t\t\tinitWG.Done()\n\t\t\treturn\n\t\t}\n\t\tinitWG.Done()   // done initializing the watch in this go routine, so the parent routine can move on...\n\t\teventsWG.Wait() // now, wait for event loop to end in this go-routine...\n\t}()\n\tinitWG.Wait() // make sure that the go routine above fully ended before returning\n}\n\n// SetConfigFile explicitly defines the path, name and extension of the config file.\n// Viper will use this and not check any of the config paths.\nfunc SetConfigFile(in string) { v.SetConfigFile(in) }\n\n// SetConfigFile explicitly defines the path, name and extension of the config file.\n// Viper will use this and not check any of the config paths.\nfunc (v *Viper) SetConfigFile(in string) {\n\tif in != \"\" {\n\t\tv.configFile = in\n\t}\n}\n\n// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.\n// E.g. if your prefix is \"spf\", the env registry will look for env\n// variables that start with \"SPF_\".\nfunc SetEnvPrefix(in string) { v.SetEnvPrefix(in) }\n\n// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.\n// E.g. if your prefix is \"spf\", the env registry will look for env\n// variables that start with \"SPF_\".\nfunc (v *Viper) SetEnvPrefix(in string) {\n\tif in != \"\" {\n\t\tv.envPrefix = in\n\t}\n}\n\n// GetEnvPrefix returns the environment variable prefix.\nfunc GetEnvPrefix() string { return v.GetEnvPrefix() }\n\n// GetEnvPrefix returns the environment variable prefix.\nfunc (v *Viper) GetEnvPrefix() string {\n\treturn v.envPrefix\n}\n\nfunc (v *Viper) mergeWithEnvPrefix(in string) string {\n\tif v.envPrefix != \"\" {\n\t\treturn strings.ToUpper(v.envPrefix + \"_\" + in)\n\t}\n\n\treturn strings.ToUpper(in)\n}\n\n// AllowEmptyEnv tells Viper to consider set,\n// but empty environment variables as valid values instead of falling back.\n// For backward compatibility reasons this is false by default.\nfunc AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) }\n\n// AllowEmptyEnv tells Viper to consider set,\n// but empty environment variables as valid values instead of falling back.\n// For backward compatibility reasons this is false by default.\nfunc (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) {\n\tv.allowEmptyEnv = allowEmptyEnv\n}\n\n// TODO: should getEnv logic be moved into find(). Can generalize the use of\n// rewriting keys many things, Ex: Get('someKey') -> some_key\n// (camel case to snake case for JSON keys perhaps)\n\n// getEnv is a wrapper around os.Getenv which replaces characters in the original\n// key. This allows env vars which have different keys than the config object\n// keys.\nfunc (v *Viper) getEnv(key string) (string, bool) {\n\tif v.envKeyReplacer != nil {\n\t\tkey = v.envKeyReplacer.Replace(key)\n\t}\n\n\tval, ok := os.LookupEnv(key)\n\n\treturn val, ok && (v.allowEmptyEnv || val != \"\")\n}\n\n// ConfigFileUsed returns the file used to populate the config registry.\nfunc ConfigFileUsed() string { return v.ConfigFileUsed() }\n\n// ConfigFileUsed returns the file used to populate the config registry.\nfunc (v *Viper) ConfigFileUsed() string { return v.configFile }\n\n// AddConfigPath adds a path for Viper to search for the config file in.\n// Can be called multiple times to define multiple search paths.\nfunc AddConfigPath(in string) { v.AddConfigPath(in) }\n\n// AddConfigPath adds a path for Viper to search for the config file in.\n// Can be called multiple times to define multiple search paths.\nfunc (v *Viper) AddConfigPath(in string) {\n\tif v.finder != nil {\n\t\tv.logger.Warn(\"ineffective call to function: custom finder takes precedence\", slog.String(\"function\", \"AddConfigPath\"))\n\t}\n\n\tif in != \"\" {\n\t\tabsin := absPathify(v.logger, in)\n\n\t\tv.logger.Info(\"adding path to search paths\", \"path\", absin)\n\t\tif !slices.Contains(v.configPaths, absin) {\n\t\t\tv.configPaths = append(v.configPaths, absin)\n\t\t}\n\t}\n}\n\n// searchMap recursively searches for a value for path in source map.\n// Returns nil if not found.\n// Note: This assumes that the path entries and map keys are lower cased.\nfunc (v *Viper) searchMap(source map[string]any, path []string) any {\n\tif len(path) == 0 {\n\t\treturn source\n\t}\n\n\tnext, ok := source[path[0]]\n\tif ok {\n\t\t// Fast path\n\t\tif len(path) == 1 {\n\t\t\treturn next\n\t\t}\n\n\t\t// Nested case\n\t\tswitch next := next.(type) {\n\t\tcase map[any]any:\n\t\t\treturn v.searchMap(cast.ToStringMap(next), path[1:])\n\t\tcase map[string]any:\n\t\t\t// Type assertion is safe here since it is only reached\n\t\t\t// if the type of `next` is the same as the type being asserted\n\t\t\treturn v.searchMap(next, path[1:])\n\t\tdefault:\n\t\t\t// got a value but nested key expected, return \"nil\" for not found\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn nil\n}\n\n// searchIndexableWithPathPrefixes recursively searches for a value for path in source map/slice.\n//\n// While searchMap() considers each path element as a single map key or slice index, this\n// function searches for, and prioritizes, merged path elements.\n// e.g., if in the source, \"foo\" is defined with a sub-key \"bar\", and \"foo.bar\"\n// is also defined, this latter value is returned for path [\"foo\", \"bar\"].\n//\n// This should be useful only at config level (other maps may not contain dots\n// in their keys).\n//\n// Note: This assumes that the path entries and map keys are lower cased.\nfunc (v *Viper) searchIndexableWithPathPrefixes(source any, path []string) any {\n\tif len(path) == 0 {\n\t\treturn source\n\t}\n\n\t// search for path prefixes, starting from the longest one\n\tfor i := len(path); i > 0; i-- {\n\t\tprefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))\n\n\t\tvar val any\n\t\tswitch sourceIndexable := source.(type) {\n\t\tcase []any:\n\t\t\tval = v.searchSliceWithPathPrefixes(sourceIndexable, prefixKey, i, path)\n\t\tcase map[string]any:\n\t\t\tval = v.searchMapWithPathPrefixes(sourceIndexable, prefixKey, i, path)\n\t\t}\n\t\tif val != nil {\n\t\t\treturn val\n\t\t}\n\t}\n\n\t// not found\n\treturn nil\n}\n\n// searchSliceWithPathPrefixes searches for a value for path in sourceSlice\n//\n// This function is part of the searchIndexableWithPathPrefixes recurring search and\n// should not be called directly from functions other than searchIndexableWithPathPrefixes.\nfunc (v *Viper) searchSliceWithPathPrefixes(\n\tsourceSlice []any,\n\tprefixKey string,\n\tpathIndex int,\n\tpath []string,\n) any {\n\t// if the prefixKey is not a number or it is out of bounds of the slice\n\tindex, err := strconv.Atoi(prefixKey)\n\tif err != nil || len(sourceSlice) <= index {\n\t\treturn nil\n\t}\n\n\tnext := sourceSlice[index]\n\n\t// Fast path\n\tif pathIndex == len(path) {\n\t\treturn next\n\t}\n\n\tswitch n := next.(type) {\n\tcase map[any]any:\n\t\treturn v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])\n\tcase map[string]any, []any:\n\t\treturn v.searchIndexableWithPathPrefixes(n, path[pathIndex:])\n\tdefault:\n\t\t// got a value but nested key expected, do nothing and look for next prefix\n\t}\n\n\t// not found\n\treturn nil\n}\n\n// searchMapWithPathPrefixes searches for a value for path in sourceMap\n//\n// This function is part of the searchIndexableWithPathPrefixes recurring search and\n// should not be called directly from functions other than searchIndexableWithPathPrefixes.\nfunc (v *Viper) searchMapWithPathPrefixes(\n\tsourceMap map[string]any,\n\tprefixKey string,\n\tpathIndex int,\n\tpath []string,\n) any {\n\tnext, ok := sourceMap[prefixKey]\n\tif !ok {\n\t\treturn nil\n\t}\n\n\t// Fast path\n\tif pathIndex == len(path) {\n\t\treturn next\n\t}\n\n\t// Nested case\n\tswitch n := next.(type) {\n\tcase map[any]any:\n\t\treturn v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])\n\tcase map[string]any, []any:\n\t\treturn v.searchIndexableWithPathPrefixes(n, path[pathIndex:])\n\tdefault:\n\t\t// got a value but nested key expected, do nothing and look for next prefix\n\t}\n\n\t// not found\n\treturn nil\n}\n\n// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere\n// on its path in the map.\n// e.g., if \"foo.bar\" has a value in the given map, it “shadows”\n//\n//\t\"foo.bar.baz\" in a lower-priority map\nfunc (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]any) string {\n\tvar parentVal any\n\tfor i := 1; i < len(path); i++ {\n\t\tparentVal = v.searchMap(m, path[0:i])\n\t\tif parentVal == nil {\n\t\t\t// not found, no need to add more path elements\n\t\t\treturn \"\"\n\t\t}\n\t\tswitch parentVal.(type) {\n\t\tcase map[any]any:\n\t\t\tcontinue\n\t\tcase map[string]any:\n\t\t\tcontinue\n\t\tdefault:\n\t\t\t// parentVal is a regular value which shadows \"path\"\n\t\t\treturn strings.Join(path[0:i], v.keyDelim)\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere\n// in a sub-path of the map.\n// e.g., if \"foo.bar\" has a value in the given map, it “shadows”\n//\n//\t\"foo.bar.baz\" in a lower-priority map\nfunc (v *Viper) isPathShadowedInFlatMap(path []string, mi any) string {\n\t// unify input map\n\tvar m map[string]interface{}\n\tswitch miv := mi.(type) {\n\tcase map[string]string:\n\t\tm = castMapStringToMapInterface(miv)\n\tcase map[string]FlagValue:\n\t\tm = castMapFlagToMapInterface(miv)\n\tdefault:\n\t\treturn \"\"\n\t}\n\n\t// scan paths\n\tvar parentKey string\n\tfor i := 1; i < len(path); i++ {\n\t\tparentKey = strings.Join(path[0:i], v.keyDelim)\n\t\tif _, ok := m[parentKey]; ok {\n\t\t\treturn parentKey\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere\n// in the environment, when automatic env is on.\n// e.g., if \"foo.bar\" has a value in the environment, it “shadows”\n//\n//\t\"foo.bar.baz\" in a lower-priority map\nfunc (v *Viper) isPathShadowedInAutoEnv(path []string) string {\n\tvar parentKey string\n\tfor i := 1; i < len(path); i++ {\n\t\tparentKey = strings.Join(path[0:i], v.keyDelim)\n\t\tif _, ok := v.getEnv(v.mergeWithEnvPrefix(parentKey)); ok {\n\t\t\treturn parentKey\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// SetTypeByDefaultValue enables or disables the inference of a key value's\n// type when the Get function is used based upon a key's default value as\n// opposed to the value returned based on the normal fetch logic.\n//\n// For example, if a key has a default value of []string{} and the same key\n// is set via an environment variable to \"a b c\", a call to the Get function\n// would return a string slice for the key if the key's type is inferred by\n// the default value and the Get function would return:\n//\n//\t[]string {\"a\", \"b\", \"c\"}\n//\n// Otherwise the Get function would return:\n//\n//\t\"a b c\"\nfunc SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }\n\n// SetTypeByDefaultValue enables or disables the inference of a key value's\n// type when the Get function is used based upon a key's default value as\n// opposed to the value returned based on the normal fetch logic.\n//\n// For example, if a key has a default value of []string{} and the same key\n// is set via an environment variable to \"a b c\", a call to the Get function\n// would return a string slice for the key if the key's type is inferred by\n// the default value and the Get function would return:\n//\n//\t[]string {\"a\", \"b\", \"c\"}\n//\n// Otherwise the Get function would return:\n//\n//\t\"a b c\"\nfunc (v *Viper) SetTypeByDefaultValue(enable bool) {\n\tv.typeByDefValue = enable\n}\n\n// GetViper gets the global Viper instance.\nfunc GetViper() *Viper {\n\treturn v\n}\n\n// Get can retrieve any value given the key to use.\n// Get is case-insensitive for a key.\n// Get has the behavior of returning the value associated with the first\n// place from where it is set. Viper will check in the following order:\n// override, flag, env, config file, key/value store, default\n//\n// Get returns an interface. For a specific value use one of the Get____ methods.\nfunc Get(key string) any { return v.Get(key) }\n\n// Get retrieves the value associated with the key.\n// Get is case-insensitive for a key.\n// Get has the behavior of returning the value associated with the first\n// place from where it is set. Viper will check in the following order:\n// override, flag, env, config file, key/value store, default\n//\n// Get returns an interface. For a specific value use one of the Get____ methods.\nfunc (v *Viper) Get(key string) any {\n\tlcaseKey := strings.ToLower(key)\n\tval := v.find(lcaseKey, true)\n\tif val == nil {\n\t\treturn nil\n\t}\n\n\tif v.typeByDefValue {\n\t\t// TODO(bep) this branch isn't covered by a single test.\n\t\tvalType := val\n\t\tpath := strings.Split(lcaseKey, v.keyDelim)\n\t\tdefVal := v.searchMap(v.defaults, path)\n\t\tif defVal != nil {\n\t\t\tvalType = defVal\n\t\t}\n\n\t\tswitch valType.(type) {\n\t\tcase bool:\n\t\t\treturn cast.ToBool(val)\n\t\tcase string:\n\t\t\treturn cast.ToString(val)\n\t\tcase int32, int16, int8, int:\n\t\t\treturn cast.ToInt(val)\n\t\tcase uint:\n\t\t\treturn cast.ToUint(val)\n\t\tcase uint32:\n\t\t\treturn cast.ToUint32(val)\n\t\tcase uint64:\n\t\t\treturn cast.ToUint64(val)\n\t\tcase int64:\n\t\t\treturn cast.ToInt64(val)\n\t\tcase float64, float32:\n\t\t\treturn cast.ToFloat64(val)\n\t\tcase time.Time:\n\t\t\treturn cast.ToTime(val)\n\t\tcase time.Duration:\n\t\t\treturn cast.ToDuration(val)\n\t\tcase []string:\n\t\t\treturn cast.ToStringSlice(val)\n\t\tcase []int:\n\t\t\treturn cast.ToIntSlice(val)\n\t\tcase []time.Duration:\n\t\t\treturn cast.ToDurationSlice(val)\n\t\t}\n\t}\n\n\treturn val\n}\n\n// Sub returns new Viper instance representing a sub tree of this instance.\n// Sub is case-insensitive for a key.\nfunc Sub(key string) *Viper { return v.Sub(key) }\n\n// Sub returns a new Viper instance representing a sub tree of this instance.\n// Sub is case-insensitive for a key.\nfunc (v *Viper) Sub(key string) *Viper {\n\tsubv := New()\n\tdata := v.Get(key)\n\tif data == nil {\n\t\treturn nil\n\t}\n\n\tif reflect.TypeOf(data).Kind() == reflect.Map {\n\t\tsubv.parents = append([]string(nil), v.parents...)\n\t\tsubv.parents = append(subv.parents, strings.ToLower(key))\n\t\tsubv.automaticEnvApplied = v.automaticEnvApplied\n\t\tsubv.envPrefix = v.envPrefix\n\t\tsubv.envKeyReplacer = v.envKeyReplacer\n\t\tsubv.keyDelim = v.keyDelim\n\t\tsubv.config = cast.ToStringMap(data)\n\t\treturn subv\n\t}\n\treturn nil\n}\n\n// GetString returns the value associated with the key as a string.\nfunc GetString(key string) string { return v.GetString(key) }\n\n// GetString returns the value associated with the key as a string.\nfunc (v *Viper) GetString(key string) string {\n\treturn cast.ToString(v.Get(key))\n}\n\n// GetBool returns the value associated with the key as a boolean.\nfunc GetBool(key string) bool { return v.GetBool(key) }\n\n// GetBool returns the value associated with the key as a boolean.\nfunc (v *Viper) GetBool(key string) bool {\n\treturn cast.ToBool(v.Get(key))\n}\n\n// GetInt returns the value associated with the key as an integer.\nfunc GetInt(key string) int { return v.GetInt(key) }\n\n// GetInt returns the value associated with the key as an integer.\nfunc (v *Viper) GetInt(key string) int {\n\treturn cast.ToInt(v.Get(key))\n}\n\n// GetInt32 returns the value associated with the key as an integer.\nfunc GetInt32(key string) int32 { return v.GetInt32(key) }\n\n// GetInt32 returns the value associated with the key as an integer.\nfunc (v *Viper) GetInt32(key string) int32 {\n\treturn cast.ToInt32(v.Get(key))\n}\n\n// GetInt64 returns the value associated with the key as an integer.\nfunc GetInt64(key string) int64 { return v.GetInt64(key) }\n\n// GetInt64 returns the value associated with the key as an integer.\nfunc (v *Viper) GetInt64(key string) int64 {\n\treturn cast.ToInt64(v.Get(key))\n}\n\n// GetUint8 returns the value associated with the key as an unsigned integer.\nfunc GetUint8(key string) uint8 { return v.GetUint8(key) }\n\n// GetUint8 returns the value associated with the key as an unsigned integer.\nfunc (v *Viper) GetUint8(key string) uint8 {\n\treturn cast.ToUint8(v.Get(key))\n}\n\n// GetUint returns the value associated with the key as an unsigned integer.\nfunc GetUint(key string) uint { return v.GetUint(key) }\n\n// GetUint returns the value associated with the key as an unsigned integer.\nfunc (v *Viper) GetUint(key string) uint {\n\treturn cast.ToUint(v.Get(key))\n}\n\n// GetUint16 returns the value associated with the key as an unsigned integer.\nfunc GetUint16(key string) uint16 { return v.GetUint16(key) }\n\n// GetUint16 returns the value associated with the key as an unsigned integer.\nfunc (v *Viper) GetUint16(key string) uint16 {\n\treturn cast.ToUint16(v.Get(key))\n}\n\n// GetUint32 returns the value associated with the key as an unsigned integer.\nfunc GetUint32(key string) uint32 { return v.GetUint32(key) }\n\n// GetUint32 returns the value associated with the key as an unsigned integer.\nfunc (v *Viper) GetUint32(key string) uint32 {\n\treturn cast.ToUint32(v.Get(key))\n}\n\n// GetUint64 returns the value associated with the key as an unsigned integer.\nfunc GetUint64(key string) uint64 { return v.GetUint64(key) }\n\n// GetUint64 returns the value associated with the key as an unsigned integer.\nfunc (v *Viper) GetUint64(key string) uint64 {\n\treturn cast.ToUint64(v.Get(key))\n}\n\n// GetFloat64 returns the value associated with the key as a float64.\nfunc GetFloat64(key string) float64 { return v.GetFloat64(key) }\n\n// GetFloat64 returns the value associated with the key as a float64.\nfunc (v *Viper) GetFloat64(key string) float64 {\n\treturn cast.ToFloat64(v.Get(key))\n}\n\n// GetTime returns the value associated with the key as time.\nfunc GetTime(key string) time.Time { return v.GetTime(key) }\n\n// GetTime returns the value associated with the key as time.\nfunc (v *Viper) GetTime(key string) time.Time {\n\treturn cast.ToTime(v.Get(key))\n}\n\n// GetDuration returns the value associated with the key as a duration.\nfunc GetDuration(key string) time.Duration { return v.GetDuration(key) }\n\n// GetDuration returns the value associated with the key as a duration.\nfunc (v *Viper) GetDuration(key string) time.Duration {\n\treturn cast.ToDuration(v.Get(key))\n}\n\n// GetIntSlice returns the value associated with the key as a slice of int values.\nfunc GetIntSlice(key string) []int { return v.GetIntSlice(key) }\n\n// GetIntSlice returns the value associated with the key as a slice of int values.\nfunc (v *Viper) GetIntSlice(key string) []int {\n\treturn cast.ToIntSlice(v.Get(key))\n}\n\n// GetStringSlice returns the value associated with the key as a slice of strings.\nfunc GetStringSlice(key string) []string { return v.GetStringSlice(key) }\n\n// GetStringSlice returns the value associated with the key as a slice of strings.\nfunc (v *Viper) GetStringSlice(key string) []string {\n\treturn cast.ToStringSlice(v.Get(key))\n}\n\n// GetStringMap returns the value associated with the key as a map of interfaces.\nfunc GetStringMap(key string) map[string]any { return v.GetStringMap(key) }\n\n// GetStringMap returns the value associated with the key as a map of interfaces.\nfunc (v *Viper) GetStringMap(key string) map[string]any {\n\treturn cast.ToStringMap(v.Get(key))\n}\n\n// GetStringMapString returns the value associated with the key as a map of strings.\nfunc GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) }\n\n// GetStringMapString returns the value associated with the key as a map of strings.\nfunc (v *Viper) GetStringMapString(key string) map[string]string {\n\treturn cast.ToStringMapString(v.Get(key))\n}\n\n// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.\nfunc GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) }\n\n// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.\nfunc (v *Viper) GetStringMapStringSlice(key string) map[string][]string {\n\treturn cast.ToStringMapStringSlice(v.Get(key))\n}\n\n// GetSizeInBytes returns the size of the value associated with the given key\n// in bytes.\nfunc GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) }\n\n// GetSizeInBytes returns the size of the value associated with the given key\n// in bytes.\nfunc (v *Viper) GetSizeInBytes(key string) uint {\n\tsizeStr := cast.ToString(v.Get(key))\n\treturn parseSizeInBytes(sizeStr)\n}\n\n// UnmarshalKey takes a single key and unmarshals it into a Struct.\nfunc UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error {\n\treturn v.UnmarshalKey(key, rawVal, opts...)\n}\n\n// UnmarshalKey takes a single key and unmarshals it into a Struct.\nfunc (v *Viper) UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error {\n\treturn decode(v.Get(key), v.defaultDecoderConfig(rawVal, opts...))\n}\n\n// Unmarshal unmarshals the config into a Struct. Make sure that the tags\n// on the fields of the structure are properly set.\nfunc Unmarshal(rawVal any, opts ...DecoderConfigOption) error {\n\treturn v.Unmarshal(rawVal, opts...)\n}\n\n// Unmarshal unmarshals the config into a Struct. Make sure that the tags\n// on the fields of the structure are properly set.\nfunc (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) error {\n\tkeys := v.AllKeys()\n\n\tif v.experimentalBindStruct {\n\t\t// TODO: make this optional?\n\t\tstructKeys, err := v.decodeStructKeys(rawVal, opts...)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tkeys = append(keys, structKeys...)\n\t}\n\n\t// TODO: struct keys should be enough?\n\treturn decode(v.getSettings(keys), v.defaultDecoderConfig(rawVal, opts...))\n}\n\nfunc (v *Viper) decodeStructKeys(input any, opts ...DecoderConfigOption) ([]string, error) {\n\tvar structKeyMap map[string]any\n\n\terr := decode(input, v.defaultDecoderConfig(&structKeyMap, opts...))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tflattenedStructKeyMap := v.flattenAndMergeMap(map[string]bool{}, structKeyMap, \"\")\n\n\tr := make([]string, 0, len(flattenedStructKeyMap))\n\tfor v := range flattenedStructKeyMap {\n\t\tr = append(r, v)\n\t}\n\n\treturn r, nil\n}\n\n// defaultDecoderConfig returns default mapstructure.DecoderConfig with support\n// of time.Duration values & string slices.\nfunc (v *Viper) defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {\n\tdecodeHook := v.decodeHook\n\tif decodeHook == nil {\n\t\tdecodeHook = mapstructure.ComposeDecodeHookFunc(\n\t\t\tmapstructure.StringToTimeDurationHookFunc(),\n\t\t\t// mapstructure.StringToSliceHookFunc(\",\"),\n\t\t\tstringToWeakSliceHookFunc(\",\"),\n\t\t)\n\t}\n\n\tc := &mapstructure.DecoderConfig{\n\t\tMetadata:         nil,\n\t\tWeaklyTypedInput: true,\n\t\tDecodeHook:       decodeHook,\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(c)\n\t}\n\n\t// Do not allow overwriting the output\n\tc.Result = output\n\n\treturn c\n}\n\n// As of mapstructure v2.0.0 StringToSliceHookFunc checks if the return type is a string slice.\n// This function removes that check.\n// TODO: implement a function that checks if the value can be converted to the return type and use it instead.\nfunc stringToWeakSliceHookFunc(sep string) mapstructure.DecodeHookFunc {\n\treturn func(\n\t\tf reflect.Type,\n\t\tt reflect.Type,\n\t\tdata interface{},\n\t) (interface{}, error) {\n\t\tif f.Kind() != reflect.String || t.Kind() != reflect.Slice {\n\t\t\treturn data, nil\n\t\t}\n\n\t\traw := data.(string)\n\t\tif raw == \"\" {\n\t\t\treturn []string{}, nil\n\t\t}\n\n\t\treturn strings.Split(raw, sep), nil\n\t}\n}\n\n// decode is a wrapper around mapstructure.Decode that mimics the WeakDecode functionality.\nfunc decode(input any, config *mapstructure.DecoderConfig) error {\n\tdecoder, err := mapstructure.NewDecoder(config)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn decoder.Decode(input)\n}\n\n// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent\n// in the destination struct.\nfunc UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {\n\treturn v.UnmarshalExact(rawVal, opts...)\n}\n\n// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent\n// in the destination struct.\nfunc (v *Viper) UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {\n\tconfig := v.defaultDecoderConfig(rawVal, opts...)\n\tconfig.ErrorUnused = true\n\n\tkeys := v.AllKeys()\n\n\tif v.experimentalBindStruct {\n\t\t// TODO: make this optional?\n\t\tstructKeys, err := v.decodeStructKeys(rawVal, opts...)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tkeys = append(keys, structKeys...)\n\t}\n\n\t// TODO: struct keys should be enough?\n\treturn decode(v.getSettings(keys), config)\n}\n\n// BindPFlags binds a full flag set to the configuration, using each flag's long\n// name as the config key.\nfunc BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) }\n\n// BindPFlags binds a full flag set to the configuration, using each flag's long\n// name as the config key.\nfunc (v *Viper) BindPFlags(flags *pflag.FlagSet) error {\n\treturn v.BindFlagValues(pflagValueSet{flags})\n}\n\n// BindPFlag binds a specific key to a pflag (as used by cobra).\n// Example (where serverCmd is a Cobra instance):\n//\n//\tserverCmd.Flags().Int(\"port\", 1138, \"Port to run Application server on\")\n//\tViper.BindPFlag(\"port\", serverCmd.Flags().Lookup(\"port\"))\nfunc BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }\n\n// BindPFlag binds a specific key to a pflag (as used by cobra).\n// Example (where serverCmd is a Cobra instance):\n//\n//\tserverCmd.Flags().Int(\"port\", 1138, \"Port to run Application server on\")\n//\tViper.BindPFlag(\"port\", serverCmd.Flags().Lookup(\"port\"))\nfunc (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {\n\tif flag == nil {\n\t\treturn fmt.Errorf(\"flag for %q is nil\", key)\n\t}\n\treturn v.BindFlagValue(key, pflagValue{flag})\n}\n\n// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long\n// name as the config key.\nfunc BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) }\n\n// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long\n// name as the config key.\nfunc (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {\n\tflags.VisitAll(func(flag FlagValue) {\n\t\tif err = v.BindFlagValue(flag.Name(), flag); err != nil {\n\t\t\treturn\n\t\t}\n\t})\n\treturn nil\n}\n\n// BindFlagValue binds a specific key to a FlagValue.\nfunc BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) }\n\n// BindFlagValue binds a specific key to a FlagValue.\nfunc (v *Viper) BindFlagValue(key string, flag FlagValue) error {\n\tif flag == nil {\n\t\treturn fmt.Errorf(\"flag for %q is nil\", key)\n\t}\n\tv.pflags[strings.ToLower(key)] = flag\n\treturn nil\n}\n\n// BindEnv binds a Viper key to a ENV variable.\n// ENV variables are case sensitive.\n// If only a key is provided, it will use the env key matching the key, uppercased.\n// If more arguments are provided, they will represent the env variable names that\n// should bind to this key and will be taken in the specified order.\n// EnvPrefix will be used when set when env name is not provided.\nfunc BindEnv(input ...string) error { return v.BindEnv(input...) }\n\n// BindEnv binds a Viper key to a ENV variable.\n// ENV variables are case sensitive.\n// If only a key is provided, it will use the env key matching the key, uppercased.\n// If more arguments are provided, they will represent the env variable names that\n// should bind to this key and will be taken in the specified order.\n// EnvPrefix will be used when set when env name is not provided.\nfunc (v *Viper) BindEnv(input ...string) error {\n\tif len(input) == 0 {\n\t\treturn fmt.Errorf(\"missing key to bind to\")\n\t}\n\n\tkey := strings.ToLower(input[0])\n\n\tif len(input) == 1 {\n\t\tv.env[key] = append(v.env[key], v.mergeWithEnvPrefix(key))\n\t} else {\n\t\tv.env[key] = append(v.env[key], input[1:]...)\n\t}\n\n\treturn nil\n}\n\n// MustBindEnv wraps BindEnv in a panic.\n// If there is an error binding an environment variable, MustBindEnv will\n// panic.\nfunc MustBindEnv(input ...string) { v.MustBindEnv(input...) }\n\n// MustBindEnv wraps BindEnv in a panic.\n// If there is an error binding an environment variable, MustBindEnv will\n// panic.\nfunc (v *Viper) MustBindEnv(input ...string) {\n\tif err := v.BindEnv(input...); err != nil {\n\t\tpanic(fmt.Sprintf(\"error while binding environment variable: %v\", err))\n\t}\n}\n\n// Given a key, find the value.\n//\n// Viper will check to see if an alias exists first.\n// Viper will then check in the following order:\n// flag, env, config file, key/value store.\n// Lastly, if no value was found and flagDefault is true, and if the key\n// corresponds to a flag, the flag's default value is returned.\n//\n// Note: this assumes a lower-cased key given.\nfunc (v *Viper) find(lcaseKey string, flagDefault bool) any {\n\tvar (\n\t\tval    any\n\t\texists bool\n\t\tpath   = strings.Split(lcaseKey, v.keyDelim)\n\t\tnested = len(path) > 1\n\t)\n\n\t// compute the path through the nested maps to the nested value\n\tif nested && v.isPathShadowedInDeepMap(path, castMapStringToMapInterface(v.aliases)) != \"\" {\n\t\treturn nil\n\t}\n\n\t// if the requested key is an alias, then return the proper key\n\tlcaseKey = v.realKey(lcaseKey)\n\tpath = strings.Split(lcaseKey, v.keyDelim)\n\tnested = len(path) > 1\n\n\t// Set() override first\n\tval = v.searchMap(v.override, path)\n\tif val != nil {\n\t\treturn val\n\t}\n\tif nested && v.isPathShadowedInDeepMap(path, v.override) != \"\" {\n\t\treturn nil\n\t}\n\n\t// PFlag override next\n\tflag, exists := v.pflags[lcaseKey]\n\tif exists && flag.HasChanged() {\n\t\tswitch flag.ValueType() {\n\t\tcase \"int\", \"int8\", \"int16\", \"int32\", \"int64\":\n\t\t\treturn cast.ToInt(flag.ValueString())\n\t\tcase \"bool\":\n\t\t\treturn cast.ToBool(flag.ValueString())\n\t\tcase \"stringSlice\", \"stringArray\":\n\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\tres, _ := readAsCSV(s)\n\t\t\treturn res\n\t\tcase \"boolSlice\":\n\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\tres, _ := readAsCSV(s)\n\t\t\treturn cast.ToBoolSlice(res)\n\t\tcase \"intSlice\":\n\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\tres, _ := readAsCSV(s)\n\t\t\treturn cast.ToIntSlice(res)\n\t\tcase \"uintSlice\":\n\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\tres, _ := readAsCSV(s)\n\t\t\treturn cast.ToUintSlice(res)\n\t\tcase \"float64Slice\":\n\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\tres, _ := readAsCSV(s)\n\t\t\treturn cast.ToFloat64Slice(res)\n\t\tcase \"durationSlice\":\n\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\tslice := strings.Split(s, \",\")\n\t\t\treturn cast.ToDurationSlice(slice)\n\t\tcase \"stringToString\":\n\t\t\treturn stringToStringConv(flag.ValueString())\n\t\tcase \"stringToInt\":\n\t\t\treturn stringToIntConv(flag.ValueString())\n\t\tdefault:\n\t\t\treturn flag.ValueString()\n\t\t}\n\t}\n\tif nested && v.isPathShadowedInFlatMap(path, v.pflags) != \"\" {\n\t\treturn nil\n\t}\n\n\t// Env override next\n\tif v.automaticEnvApplied {\n\t\tenvKey := strings.Join(append(v.parents, lcaseKey), \".\")\n\t\t// even if it hasn't been registered, if automaticEnv is used,\n\t\t// check any Get request\n\t\tif val, ok := v.getEnv(v.mergeWithEnvPrefix(envKey)); ok {\n\t\t\treturn val\n\t\t}\n\t\tif nested && v.isPathShadowedInAutoEnv(path) != \"\" {\n\t\t\treturn nil\n\t\t}\n\t}\n\tenvkeys, exists := v.env[lcaseKey]\n\tif exists {\n\t\tfor _, envkey := range envkeys {\n\t\t\tif val, ok := v.getEnv(envkey); ok {\n\t\t\t\treturn val\n\t\t\t}\n\t\t}\n\t}\n\tif nested && v.isPathShadowedInFlatMap(path, v.env) != \"\" {\n\t\treturn nil\n\t}\n\n\t// Config file next\n\tval = v.searchIndexableWithPathPrefixes(v.config, path)\n\tif val != nil {\n\t\treturn val\n\t}\n\tif nested && v.isPathShadowedInDeepMap(path, v.config) != \"\" {\n\t\treturn nil\n\t}\n\n\t// K/V store next\n\tval = v.searchMap(v.kvstore, path)\n\tif val != nil {\n\t\treturn val\n\t}\n\tif nested && v.isPathShadowedInDeepMap(path, v.kvstore) != \"\" {\n\t\treturn nil\n\t}\n\n\t// Default next\n\tval = v.searchMap(v.defaults, path)\n\tif val != nil {\n\t\treturn val\n\t}\n\tif nested && v.isPathShadowedInDeepMap(path, v.defaults) != \"\" {\n\t\treturn nil\n\t}\n\n\tif flagDefault {\n\t\t// last chance: if no value is found and a flag does exist for the key,\n\t\t// get the flag's default value even if the flag's value has not been set.\n\t\tif flag, exists := v.pflags[lcaseKey]; exists {\n\t\t\tswitch flag.ValueType() {\n\t\t\tcase \"int\", \"int8\", \"int16\", \"int32\", \"int64\":\n\t\t\t\treturn cast.ToInt(flag.ValueString())\n\t\t\tcase \"bool\":\n\t\t\t\treturn cast.ToBool(flag.ValueString())\n\t\t\tcase \"stringSlice\", \"stringArray\":\n\t\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\t\tres, _ := readAsCSV(s)\n\t\t\t\treturn res\n\t\t\tcase \"boolSlice\":\n\t\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\t\tres, _ := readAsCSV(s)\n\t\t\t\treturn cast.ToBoolSlice(res)\n\t\t\tcase \"intSlice\":\n\t\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\t\tres, _ := readAsCSV(s)\n\t\t\t\treturn cast.ToIntSlice(res)\n\t\t\tcase \"uintSlice\":\n\t\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\t\tres, _ := readAsCSV(s)\n\t\t\t\treturn cast.ToUintSlice(res)\n\t\t\tcase \"float64Slice\":\n\t\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\t\tres, _ := readAsCSV(s)\n\t\t\t\treturn cast.ToFloat64Slice(res)\n\t\t\tcase \"stringToString\":\n\t\t\t\treturn stringToStringConv(flag.ValueString())\n\t\t\tcase \"stringToInt\":\n\t\t\t\treturn stringToIntConv(flag.ValueString())\n\t\t\tcase \"durationSlice\":\n\t\t\t\ts := strings.TrimPrefix(flag.ValueString(), \"[\")\n\t\t\t\ts = strings.TrimSuffix(s, \"]\")\n\t\t\t\tslice := strings.Split(s, \",\")\n\t\t\t\treturn cast.ToDurationSlice(slice)\n\t\t\tdefault:\n\t\t\t\treturn flag.ValueString()\n\t\t\t}\n\t\t}\n\t\t// last item, no need to check shadowing\n\t}\n\n\treturn nil\n}\n\nfunc readAsCSV(val string) ([]string, error) {\n\tif val == \"\" {\n\t\treturn []string{}, nil\n\t}\n\tstringReader := strings.NewReader(val)\n\tcsvReader := csv.NewReader(stringReader)\n\treturn csvReader.Read()\n}\n\n// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/master/string_to_string.go#L79\n// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap.\nfunc stringToStringConv(val string) any {\n\tval = strings.Trim(val, \"[]\")\n\t// An empty string would cause an empty map\n\tif val == \"\" {\n\t\treturn map[string]any{}\n\t}\n\tr := csv.NewReader(strings.NewReader(val))\n\tss, err := r.Read()\n\tif err != nil {\n\t\treturn nil\n\t}\n\tout := make(map[string]any, len(ss))\n\tfor _, pair := range ss {\n\t\tk, vv, found := strings.Cut(pair, \"=\")\n\t\tif !found {\n\t\t\treturn nil\n\t\t}\n\t\tout[k] = vv\n\t}\n\treturn out\n}\n\n// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/d5e0c0615acee7028e1e2740a11102313be88de1/string_to_int.go#L68\n// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap.\nfunc stringToIntConv(val string) any {\n\tval = strings.Trim(val, \"[]\")\n\t// An empty string would cause an empty map\n\tif val == \"\" {\n\t\treturn map[string]any{}\n\t}\n\tss := strings.Split(val, \",\")\n\tout := make(map[string]any, len(ss))\n\tfor _, pair := range ss {\n\t\tk, vv, found := strings.Cut(pair, \"=\")\n\t\tif !found {\n\t\t\treturn nil\n\t\t}\n\t\tvar err error\n\t\tout[k], err = strconv.Atoi(vv)\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn out\n}\n\n// IsSet checks to see if the key has been set in any of the data locations.\n// IsSet is case-insensitive for a key.\nfunc IsSet(key string) bool { return v.IsSet(key) }\n\n// IsSet checks to see if the key has been set in any of the data locations.\n// IsSet is case-insensitive for a key.\nfunc (v *Viper) IsSet(key string) bool {\n\tlcaseKey := strings.ToLower(key)\n\tval := v.find(lcaseKey, false)\n\treturn val != nil\n}\n\n// AutomaticEnv makes Viper check if environment variables match any of the existing keys\n// (config, default or flags). If matching env vars are found, they are loaded into Viper.\nfunc AutomaticEnv() { v.AutomaticEnv() }\n\n// AutomaticEnv makes Viper check if environment variables match any of the existing keys\n// (config, default or flags). If matching env vars are found, they are loaded into Viper.\nfunc (v *Viper) AutomaticEnv() {\n\tv.automaticEnvApplied = true\n}\n\n// SetEnvKeyReplacer sets the strings.Replacer on the viper object\n// Useful for mapping an environmental variable to a key that does\n// not match it.\nfunc SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) }\n\n// SetEnvKeyReplacer sets the strings.Replacer on the viper object\n// Useful for mapping an environmental variable to a key that does\n// not match it.\nfunc (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {\n\tv.envKeyReplacer = r\n}\n\n// RegisterAlias creates an alias that provides another accessor for the same key.\n// This enables one to change a name without breaking the application.\nfunc RegisterAlias(alias, key string) { v.RegisterAlias(alias, key) }\n\n// RegisterAlias creates an alias that provides another accessor for the same key.\n// This enables one to change a name without breaking the application.\nfunc (v *Viper) RegisterAlias(alias, key string) {\n\tv.registerAlias(alias, strings.ToLower(key))\n}\n\nfunc (v *Viper) registerAlias(alias, key string) {\n\talias = strings.ToLower(alias)\n\tif alias != key && alias != v.realKey(key) {\n\t\t_, exists := v.aliases[alias]\n\n\t\tif !exists {\n\t\t\t// if we alias something that exists in one of the maps to another\n\t\t\t// name, we'll never be able to get that value using the original\n\t\t\t// name, so move the config value to the new realkey.\n\t\t\tif val, ok := v.config[alias]; ok {\n\t\t\t\tdelete(v.config, alias)\n\t\t\t\tv.config[key] = val\n\t\t\t}\n\t\t\tif val, ok := v.kvstore[alias]; ok {\n\t\t\t\tdelete(v.kvstore, alias)\n\t\t\t\tv.kvstore[key] = val\n\t\t\t}\n\t\t\tif val, ok := v.defaults[alias]; ok {\n\t\t\t\tdelete(v.defaults, alias)\n\t\t\t\tv.defaults[key] = val\n\t\t\t}\n\t\t\tif val, ok := v.override[alias]; ok {\n\t\t\t\tdelete(v.override, alias)\n\t\t\t\tv.override[key] = val\n\t\t\t}\n\t\t\tv.aliases[alias] = key\n\t\t}\n\t} else {\n\t\tv.logger.Warn(\"creating circular reference alias\", \"alias\", alias, \"key\", key, \"real_key\", v.realKey(key))\n\t}\n}\n\nfunc (v *Viper) realKey(key string) string {\n\tnewkey, exists := v.aliases[key]\n\tif exists {\n\t\tv.logger.Debug(\"key is an alias\", \"alias\", key, \"to\", newkey)\n\n\t\treturn v.realKey(newkey)\n\t}\n\treturn key\n}\n\n// InConfig checks to see if the given key (or an alias) is in the config file.\nfunc InConfig(key string) bool { return v.InConfig(key) }\n\n// InConfig checks to see if the given key (or an alias) is in the config file.\nfunc (v *Viper) InConfig(key string) bool {\n\tlcaseKey := strings.ToLower(key)\n\n\t// if the requested key is an alias, then return the proper key\n\tlcaseKey = v.realKey(lcaseKey)\n\tpath := strings.Split(lcaseKey, v.keyDelim)\n\n\treturn v.searchIndexableWithPathPrefixes(v.config, path) != nil\n}\n\n// SetDefault sets the default value for this key.\n// SetDefault is case-insensitive for a key.\n// Default only used when no value is provided by the user via flag, config or ENV.\nfunc SetDefault(key string, value any) { v.SetDefault(key, value) }\n\n// SetDefault sets the default value for this key.\n// SetDefault is case-insensitive for a key.\n// Default only used when no value is provided by the user via flag, config or ENV.\nfunc (v *Viper) SetDefault(key string, value any) {\n\t// If alias passed in, then set the proper default\n\tkey = v.realKey(strings.ToLower(key))\n\tvalue = toCaseInsensitiveValue(value)\n\n\tpath := strings.Split(key, v.keyDelim)\n\tlastKey := strings.ToLower(path[len(path)-1])\n\tdeepestMap := deepSearch(v.defaults, path[0:len(path)-1])\n\n\t// set innermost value\n\tdeepestMap[lastKey] = value\n}\n\n// Set sets the value for the key in the override register.\n// Set is case-insensitive for a key.\n// Will be used instead of values obtained via\n// flags, config file, ENV, default, or key/value store.\nfunc Set(key string, value any) { v.Set(key, value) }\n\n// Set sets the value for the key in the override register.\n// Set is case-insensitive for a key.\n// Will be used instead of values obtained via\n// flags, config file, ENV, default, or key/value store.\nfunc (v *Viper) Set(key string, value any) {\n\t// If alias passed in, then set the proper override\n\tkey = v.realKey(strings.ToLower(key))\n\tvalue = toCaseInsensitiveValue(value)\n\n\tpath := strings.Split(key, v.keyDelim)\n\tlastKey := strings.ToLower(path[len(path)-1])\n\tdeepestMap := deepSearch(v.override, path[0:len(path)-1])\n\n\t// set innermost value\n\tdeepestMap[lastKey] = value\n}\n\n// ReadInConfig will discover and load the configuration file from disk\n// and key/value stores, searching in one of the defined paths.\nfunc ReadInConfig() error { return v.ReadInConfig() }\n\n// ReadInConfig will discover and load the configuration file from disk\n// and key/value stores, searching in one of the defined paths.\nfunc (v *Viper) ReadInConfig() error {\n\tv.logger.Info(\"attempting to read in config file\")\n\tfilename, err := v.getConfigFile()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !slices.Contains(SupportedExts, v.getConfigType()) {\n\t\treturn UnsupportedConfigError(v.getConfigType())\n\t}\n\n\tv.logger.Debug(\"reading file\", \"file\", filename)\n\n\tfile, err := afero.ReadFile(v.fs, filename)\n\tif errors.Is(err, fs.ErrNotExist) {\n\t\t// The specified config file is missing\n\t\treturn FileNotFoundError{err: err, path: filename}\n\t} else if err != nil {\n\t\t// We hit some other error from the filesystem that isn't a missing file\n\t\treturn err\n\t}\n\n\tconfig := make(map[string]any)\n\n\terr = v.unmarshalReader(bytes.NewReader(file), config)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tv.config = config\n\treturn nil\n}\n\n// MergeInConfig merges a new configuration with an existing config.\nfunc MergeInConfig() error { return v.MergeInConfig() }\n\n// MergeInConfig merges a new configuration with an existing config.\nfunc (v *Viper) MergeInConfig() error {\n\tv.logger.Info(\"attempting to merge in config file\")\n\tfilename, err := v.getConfigFile()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !slices.Contains(SupportedExts, v.getConfigType()) {\n\t\treturn UnsupportedConfigError(v.getConfigType())\n\t}\n\n\tfile, err := afero.ReadFile(v.fs, filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn v.MergeConfig(bytes.NewReader(file))\n}\n\n// ReadConfig will read a configuration file, setting existing keys to nil if the\n// key does not exist in the file.\nfunc ReadConfig(in io.Reader) error { return v.ReadConfig(in) }\n\n// ReadConfig reads a configuration file, setting existing keys to nil if the\n// key does not exist in the file.\nfunc (v *Viper) ReadConfig(in io.Reader) error {\n\tconfig := make(map[string]any)\n\n\terr := v.unmarshalReader(in, config)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tv.config = config\n\n\treturn nil\n}\n\n// MergeConfig merges a new configuration with an existing config.\nfunc MergeConfig(in io.Reader) error { return v.MergeConfig(in) }\n\n// MergeConfig merges a new configuration with an existing config.\nfunc (v *Viper) MergeConfig(in io.Reader) error {\n\tconfig := make(map[string]any)\n\n\tif err := v.unmarshalReader(in, config); err != nil {\n\t\treturn err\n\t}\n\n\treturn v.MergeConfigMap(config)\n}\n\n// MergeConfigMap merges the configuration from the map given with an existing config.\n// Note that the map given may be modified.\nfunc MergeConfigMap(cfg map[string]any) error { return v.MergeConfigMap(cfg) }\n\n// MergeConfigMap merges the configuration from the map given with an existing config.\n// Note that the map given may be modified.\nfunc (v *Viper) MergeConfigMap(cfg map[string]any) error {\n\tif v.config == nil {\n\t\tv.config = make(map[string]any)\n\t}\n\tinsensitiviseMap(cfg)\n\tmergeMaps(cfg, v.config, nil)\n\treturn nil\n}\n\n// WriteConfig writes the current configuration to a file.\nfunc WriteConfig() error { return v.WriteConfig() }\n\n// WriteConfig writes the current configuration to a file.\nfunc (v *Viper) WriteConfig() error {\n\tfilename, err := v.getConfigFile()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn v.writeConfig(filename, true)\n}\n\n// SafeWriteConfig writes current configuration to file only if the file does not exist.\nfunc SafeWriteConfig() error { return v.SafeWriteConfig() }\n\n// SafeWriteConfig writes current configuration to file only if the file does not exist.\nfunc (v *Viper) SafeWriteConfig() error {\n\tif len(v.configPaths) < 1 {\n\t\treturn errors.New(\"missing configuration for 'configPath'\")\n\t}\n\treturn v.SafeWriteConfigAs(filepath.Join(v.configPaths[0], v.configName+\".\"+v.configType))\n}\n\n// WriteConfigAs writes current configuration to a given filename.\nfunc WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) }\n\n// WriteConfigAs writes current configuration to a given filename.\nfunc (v *Viper) WriteConfigAs(filename string) error {\n\treturn v.writeConfig(filename, true)\n}\n\n// WriteConfigTo writes current configuration to an [io.Writer].\nfunc WriteConfigTo(w io.Writer) error { return v.WriteConfigTo(w) }\n\n// WriteConfigTo writes current configuration to an [io.Writer].\nfunc (v *Viper) WriteConfigTo(w io.Writer) error {\n\tformat := strings.ToLower(v.getConfigType())\n\n\tif !slices.Contains(SupportedExts, format) {\n\t\treturn UnsupportedConfigError(format)\n\t}\n\n\treturn v.marshalWriter(w, format)\n}\n\n// SafeWriteConfigAs writes current configuration to a given filename if it does not exist.\nfunc SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }\n\n// SafeWriteConfigAs writes current configuration to a given filename if it does not exist.\nfunc (v *Viper) SafeWriteConfigAs(filename string) error {\n\talreadyExists, err := afero.Exists(v.fs, filename)\n\tif alreadyExists && err == nil {\n\t\treturn ConfigFileAlreadyExistsError(filename)\n\t}\n\treturn v.writeConfig(filename, false)\n}\n\nfunc (v *Viper) writeConfig(filename string, force bool) error {\n\tv.logger.Info(\"attempting to write configuration to file\")\n\n\tvar configType string\n\n\text := filepath.Ext(filename)\n\tif ext != \"\" && ext != filepath.Base(filename) {\n\t\tconfigType = ext[1:]\n\t} else {\n\t\tconfigType = v.configType\n\t}\n\tif configType == \"\" {\n\t\treturn fmt.Errorf(\"config type could not be determined for %s\", filename)\n\t}\n\n\tif !slices.Contains(SupportedExts, configType) {\n\t\treturn UnsupportedConfigError(configType)\n\t}\n\tif v.config == nil {\n\t\tv.config = make(map[string]any)\n\t}\n\tflags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY\n\tif !force {\n\t\tflags |= os.O_EXCL\n\t}\n\tf, err := v.fs.OpenFile(filename, flags, v.configPermissions)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\tif err := v.marshalWriter(f, configType); err != nil {\n\t\treturn err\n\t}\n\n\treturn f.Sync()\n}\n\nfunc (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error {\n\tformat := strings.ToLower(v.getConfigType())\n\tif format == \"\" {\n\t\treturn errors.New(\"cannot decode configuration: unable to determine config type\")\n\t}\n\n\tbuf := new(bytes.Buffer)\n\t_, err := buf.ReadFrom(in)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read configuration from input: %w\", err)\n\t}\n\n\t// TODO: remove this once SupportedExts is deprecated/removed\n\tif !slices.Contains(SupportedExts, format) {\n\t\treturn UnsupportedConfigError(format)\n\t}\n\n\t// TODO: return [UnsupportedConfigError] if the registry does not contain the format\n\t// TODO: consider deprecating this error type\n\tdecoder, err := v.decoderRegistry.Decoder(format)\n\tif err != nil {\n\t\treturn ConfigParseError{err}\n\t}\n\n\terr = decoder.Decode(buf.Bytes(), c)\n\tif err != nil {\n\t\treturn ConfigParseError{err}\n\t}\n\n\tinsensitiviseMap(c)\n\treturn nil\n}\n\n// Marshal a map into Writer.\nfunc (v *Viper) marshalWriter(w io.Writer, configType string) error {\n\tc := v.AllSettings()\n\n\tencoder, err := v.encoderRegistry.Encoder(configType)\n\tif err != nil {\n\t\treturn ConfigMarshalError{err}\n\t}\n\n\tb, err := encoder.Encode(c)\n\tif err != nil {\n\t\treturn ConfigMarshalError{err}\n\t}\n\n\t_, err = w.Write(b)\n\tif err != nil {\n\t\treturn ConfigMarshalError{err}\n\t}\n\n\treturn nil\n}\n\nfunc keyExists(k string, m map[string]any) string {\n\tlk := strings.ToLower(k)\n\tfor mk := range m {\n\t\tlmk := strings.ToLower(mk)\n\t\tif lmk == lk {\n\t\t\treturn mk\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc castToMapStringInterface(\n\tsrc map[any]any,\n) map[string]any {\n\ttgt := map[string]any{}\n\tfor k, v := range src {\n\t\ttgt[fmt.Sprintf(\"%v\", k)] = v\n\t}\n\treturn tgt\n}\n\nfunc castMapStringSliceToMapInterface(src map[string][]string) map[string]any {\n\ttgt := map[string]any{}\n\tfor k, v := range src {\n\t\ttgt[k] = v\n\t}\n\treturn tgt\n}\n\nfunc castMapStringToMapInterface(src map[string]string) map[string]any {\n\ttgt := map[string]any{}\n\tfor k, v := range src {\n\t\ttgt[k] = v\n\t}\n\treturn tgt\n}\n\nfunc castMapFlagToMapInterface(src map[string]FlagValue) map[string]any {\n\ttgt := map[string]any{}\n\tfor k, v := range src {\n\t\ttgt[k] = v\n\t}\n\treturn tgt\n}\n\n// mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's\n// insistence on parsing nested structures as `map[any]any`\n// instead of using a `string` as the key for nest structures beyond one level\n// deep. Both map types are supported as there is a go-yaml fork that uses\n// `map[string]any` instead.\nfunc mergeMaps(src, tgt map[string]any, itgt map[any]any) {\n\tfor sk, sv := range src {\n\t\ttk := keyExists(sk, tgt)\n\t\tif tk == \"\" {\n\t\t\tv.logger.Debug(\"\", \"tk\", \"\\\"\\\"\", fmt.Sprintf(\"tgt[%s]\", sk), sv)\n\t\t\ttgt[sk] = sv\n\t\t\tif itgt != nil {\n\t\t\t\titgt[sk] = sv\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\ttv, ok := tgt[tk]\n\t\tif !ok {\n\t\t\tv.logger.Debug(\"\", fmt.Sprintf(\"ok[%s]\", tk), false, fmt.Sprintf(\"tgt[%s]\", sk), sv)\n\t\t\ttgt[sk] = sv\n\t\t\tif itgt != nil {\n\t\t\t\titgt[sk] = sv\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tsvType := reflect.TypeOf(sv)\n\t\ttvType := reflect.TypeOf(tv)\n\n\t\tv.logger.Debug(\n\t\t\t\"processing\",\n\t\t\t\"key\", sk,\n\t\t\t\"st\", svType,\n\t\t\t\"tt\", tvType,\n\t\t\t\"sv\", sv,\n\t\t\t\"tv\", tv,\n\t\t)\n\n\t\tswitch ttv := tv.(type) {\n\t\tcase map[any]any:\n\t\t\tv.logger.Debug(\"merging maps (must convert)\")\n\t\t\ttsv, ok := sv.(map[any]any)\n\t\t\tif !ok {\n\t\t\t\tv.logger.Error(\n\t\t\t\t\t\"Could not cast sv to map[any]any\",\n\t\t\t\t\t\"key\", sk,\n\t\t\t\t\t\"st\", svType,\n\t\t\t\t\t\"tt\", tvType,\n\t\t\t\t\t\"sv\", sv,\n\t\t\t\t\t\"tv\", tv,\n\t\t\t\t)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tssv := castToMapStringInterface(tsv)\n\t\t\tstv := castToMapStringInterface(ttv)\n\t\t\tmergeMaps(ssv, stv, ttv)\n\t\tcase map[string]any:\n\t\t\tv.logger.Debug(\"merging maps\")\n\t\t\ttsv, ok := sv.(map[string]any)\n\t\t\tif !ok {\n\t\t\t\tv.logger.Error(\n\t\t\t\t\t\"Could not cast sv to map[string]any\",\n\t\t\t\t\t\"key\", sk,\n\t\t\t\t\t\"st\", svType,\n\t\t\t\t\t\"tt\", tvType,\n\t\t\t\t\t\"sv\", sv,\n\t\t\t\t\t\"tv\", tv,\n\t\t\t\t)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tmergeMaps(tsv, ttv, nil)\n\t\tdefault:\n\t\t\tv.logger.Debug(\"setting value\")\n\t\t\ttgt[tk] = sv\n\t\t\tif itgt != nil {\n\t\t\t\titgt[tk] = sv\n\t\t\t}\n\t\t}\n\t}\n}\n\n// AllKeys returns all keys holding a value, regardless of where they are set.\n// Nested keys are returned with a v.keyDelim separator.\nfunc AllKeys() []string { return v.AllKeys() }\n\n// AllKeys returns all keys holding a value, regardless of where they are set.\n// Nested keys are returned with a v.keyDelim separator.\nfunc (v *Viper) AllKeys() []string {\n\tm := map[string]bool{}\n\t// add all paths, by order of descending priority to ensure correct shadowing\n\tm = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), \"\")\n\tm = v.flattenAndMergeMap(m, v.override, \"\")\n\tm = v.mergeFlatMap(m, castMapFlagToMapInterface(v.pflags))\n\tm = v.mergeFlatMap(m, castMapStringSliceToMapInterface(v.env))\n\tm = v.flattenAndMergeMap(m, v.config, \"\")\n\tm = v.flattenAndMergeMap(m, v.kvstore, \"\")\n\tm = v.flattenAndMergeMap(m, v.defaults, \"\")\n\n\t// convert set of paths to list\n\ta := make([]string, 0, len(m))\n\tfor x := range m {\n\t\ta = append(a, x)\n\t}\n\treturn a\n}\n\n// flattenAndMergeMap recursively flattens the given map into a map[string]bool\n// of key paths (used as a set, easier to manipulate than a []string):\n//   - each path is merged into a single key string, delimited with v.keyDelim\n//   - if a path is shadowed by an earlier value in the initial shadow map,\n//     it is skipped.\n//\n// The resulting set of paths is merged to the given shadow set at the same time.\nfunc (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]any, prefix string) map[string]bool {\n\tif shadow != nil && prefix != \"\" && shadow[prefix] {\n\t\t// prefix is shadowed => nothing more to flatten\n\t\treturn shadow\n\t}\n\tif shadow == nil {\n\t\tshadow = make(map[string]bool)\n\t}\n\n\tvar m2 map[string]any\n\tif prefix != \"\" {\n\t\tprefix += v.keyDelim\n\t}\n\tfor k, val := range m {\n\t\tfullKey := prefix + k\n\t\tswitch val := val.(type) {\n\t\tcase map[string]any:\n\t\t\tm2 = val\n\t\tcase map[any]any:\n\t\t\tm2 = cast.ToStringMap(val)\n\t\tdefault:\n\t\t\t// immediate value\n\t\t\tshadow[strings.ToLower(fullKey)] = true\n\t\t\tcontinue\n\t\t}\n\t\t// recursively merge to shadow map\n\t\tshadow = v.flattenAndMergeMap(shadow, m2, fullKey)\n\t}\n\treturn shadow\n}\n\n// mergeFlatMap merges the given maps, excluding values of the second map\n// shadowed by values from the first map.\nfunc (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]any) map[string]bool {\n\t// scan keys\nouter:\n\tfor k := range m {\n\t\tpath := strings.Split(k, v.keyDelim)\n\t\t// scan intermediate paths\n\t\tvar parentKey string\n\t\tfor i := 1; i < len(path); i++ {\n\t\t\tparentKey = strings.Join(path[0:i], v.keyDelim)\n\t\t\tif shadow[parentKey] {\n\t\t\t\t// path is shadowed, continue\n\t\t\t\tcontinue outer\n\t\t\t}\n\t\t}\n\t\t// add key\n\t\tshadow[strings.ToLower(k)] = true\n\t}\n\treturn shadow\n}\n\n// AllSettings merges all settings and returns them as a map[string]any.\nfunc AllSettings() map[string]any { return v.AllSettings() }\n\n// AllSettings merges all settings and returns them as a map[string]any.\nfunc (v *Viper) AllSettings() map[string]any {\n\treturn v.getSettings(v.AllKeys())\n}\n\nfunc (v *Viper) getSettings(keys []string) map[string]any {\n\tm := map[string]any{}\n\t// start from the list of keys, and construct the map one value at a time\n\tfor _, k := range keys {\n\t\tvalue := v.Get(k)\n\t\tif value == nil {\n\t\t\t// should not happen, since AllKeys() returns only keys holding a value,\n\t\t\t// check just in case anything changes\n\t\t\tcontinue\n\t\t}\n\t\tpath := strings.Split(k, v.keyDelim)\n\t\tlastKey := strings.ToLower(path[len(path)-1])\n\t\tdeepestMap := deepSearch(m, path[0:len(path)-1])\n\t\t// set innermost value\n\t\tdeepestMap[lastKey] = value\n\t}\n\treturn m\n}\n\n// SetFs sets the filesystem to use to read configuration.\nfunc SetFs(fs afero.Fs) { v.SetFs(fs) }\n\n// SetFs sets the filesystem to use to read configuration.\nfunc (v *Viper) SetFs(fs afero.Fs) {\n\tv.fs = fs\n}\n\n// SetConfigName sets name for the config file.\n// Does not include extension.\nfunc SetConfigName(in string) { v.SetConfigName(in) }\n\n// SetConfigName sets the name for the config file.\n// Does not include extension.\nfunc (v *Viper) SetConfigName(in string) {\n\tif v.finder != nil {\n\t\tv.logger.Warn(\"ineffective call to function: custom finder takes precedence\", slog.String(\"function\", \"SetConfigName\"))\n\t}\n\n\tif in != \"\" {\n\t\tv.configName = in\n\t\tv.configFile = \"\"\n\t}\n}\n\n// SetConfigType sets the type of the configuration returned by the\n// remote source, e.g. \"json\".\nfunc SetConfigType(in string) { v.SetConfigType(in) }\n\n// SetConfigType sets the type of the configuration returned by the\n// remote source, e.g. \"json\".\nfunc (v *Viper) SetConfigType(in string) {\n\tif in != \"\" {\n\t\tv.configType = in\n\t}\n}\n\n// SetConfigPermissions sets the permissions for the config file.\nfunc SetConfigPermissions(perm os.FileMode) { v.SetConfigPermissions(perm) }\n\n// SetConfigPermissions sets the permissions for the config file.\nfunc (v *Viper) SetConfigPermissions(perm os.FileMode) {\n\tv.configPermissions = perm.Perm()\n}\n\nfunc (v *Viper) getConfigType() string {\n\tif v.configType != \"\" {\n\t\treturn v.configType\n\t}\n\n\tcf, err := v.getConfigFile()\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\n\text := filepath.Ext(cf)\n\n\tif len(ext) > 1 {\n\t\treturn ext[1:]\n\t}\n\n\treturn \"\"\n}\n\nfunc (v *Viper) getConfigFile() (string, error) {\n\tif v.configFile == \"\" {\n\t\tcf, err := v.findConfigFile()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tv.configFile = cf\n\t}\n\treturn v.configFile, nil\n}\n\n// Debug prints all configuration registries for debugging\n// purposes.\nfunc Debug() { v.Debug() }\n\n// DebugTo writes all configuration registries to the given [io.Writer].\nfunc DebugTo(w io.Writer) { v.DebugTo(w) }\n\n// Debug prints all configuration registries for debugging purposes.\nfunc (v *Viper) Debug() { v.DebugTo(os.Stdout) }\n\n// DebugTo writes all configuration registries to the given [io.Writer].\nfunc (v *Viper) DebugTo(w io.Writer) {\n\tfmt.Fprintf(w, \"Aliases:\\n%#v\\n\", v.aliases)\n\tfmt.Fprintf(w, \"Override:\\n%#v\\n\", v.override)\n\tfmt.Fprintf(w, \"PFlags:\\n%#v\\n\", v.pflags)\n\tfmt.Fprintf(w, \"Env:\\n%#v\\n\", v.env)\n\tfmt.Fprintf(w, \"Key/Value Store:\\n%#v\\n\", v.kvstore)\n\tfmt.Fprintf(w, \"Config:\\n%#v\\n\", v.config)\n\tfmt.Fprintf(w, \"Defaults:\\n%#v\\n\", v.defaults)\n}\n"
  },
  {
    "path": "viper_test.go",
    "content": "// Copyright © 2014 Steve Francia <spf@spf13.com>.\n//\n// Use of this source code is governed by an MIT-style\n// license that can be found in the LICENSE file.\n\npackage viper\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/go-viper/mapstructure/v2\"\n\t\"github.com/sagikazarmark/locafero\"\n\t\"github.com/spf13/afero\"\n\t\"github.com/spf13/cast\"\n\t\"github.com/spf13/pflag\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/spf13/viper/internal/testutil\"\n)\n\n// var yamlExample = []byte(`Hacker: true\n// name: steve\n// hobbies:\n//     - skateboarding\n//     - snowboarding\n//     - go\n// clothing:\n//     jacket: leather\n//     trousers: denim\n//     pants:\n//         size: large\n// age: 35\n// eyes : brown\n// beard: true\n// `)\n\nvar yamlExampleWithExtras = []byte(`Existing: true\nBogus: true\n`)\n\ntype testUnmarshalExtra struct {\n\tExisting bool\n}\n\nvar tomlExample = []byte(`\ntitle = \"TOML Example\"\n\n[owner]\norganization = \"MongoDB\"\nBio = \"MongoDB Chief Developer Advocate & Hacker at Large\"\ndob = 1979-05-27T07:32:00Z # First class dates? Why not?`)\n\nvar dotenvExample = []byte(`\nTITLE_DOTENV=\"DotEnv Example\"\nTYPE_DOTENV=donut\nNAME_DOTENV=Cake`)\n\nvar jsonExample = []byte(`{\n\"id\": \"0001\",\n\"type\": \"donut\",\n\"name\": \"Cake\",\n\"ppu\": 0.55,\n\"batters\": {\n        \"batter\": [\n                { \"type\": \"Regular\" },\n                { \"type\": \"Chocolate\" },\n                { \"type\": \"Blueberry\" },\n                { \"type\": \"Devil's Food\" }\n            ]\n    }\n}`)\n\nvar remoteExample = []byte(`{\n\"id\":\"0002\",\n\"type\":\"cronut\",\n\"newkey\":\"remote\"\n}`)\n\nfunc initConfigs(v *Viper) {\n\tvar r io.Reader\n\tv.SetConfigType(\"yaml\")\n\tr = bytes.NewReader(yamlExample)\n\tv.unmarshalReader(r, v.config)\n\n\tv.SetConfigType(\"json\")\n\tr = bytes.NewReader(jsonExample)\n\tv.unmarshalReader(r, v.config)\n\n\tv.SetConfigType(\"toml\")\n\tr = bytes.NewReader(tomlExample)\n\tv.unmarshalReader(r, v.config)\n\n\tv.SetConfigType(\"env\")\n\tr = bytes.NewReader(dotenvExample)\n\tv.unmarshalReader(r, v.config)\n\n\tv.SetConfigType(\"json\")\n\tremote := bytes.NewReader(remoteExample)\n\tv.unmarshalReader(remote, v.kvstore)\n}\n\nfunc initConfig(typ, config string, v *Viper) {\n\tv.SetConfigType(typ)\n\tr := strings.NewReader(config)\n\n\tif err := v.unmarshalReader(r, v.config); err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// initDirs makes directories for testing.\nfunc initDirs(t *testing.T) (string, string) {\n\tvar (\n\t\ttestDirs = []string{`a a`, `b`, `C_`}\n\t\tconfig   = `improbable`\n\t)\n\n\tif runtime.GOOS != \"windows\" {\n\t\ttestDirs = append(testDirs, `d\\d`)\n\t}\n\n\troot := t.TempDir()\n\n\tfor _, dir := range testDirs {\n\t\tinnerDir := filepath.Join(root, dir)\n\t\terr := os.Mkdir(innerDir, 0o750)\n\t\trequire.NoError(t, err)\n\n\t\terr = os.WriteFile(\n\t\t\tfilepath.Join(innerDir, config+\".toml\"),\n\t\t\t[]byte(`key = \"value is `+dir+`\"`+\"\\n\"),\n\t\t\t0o640)\n\t\trequire.NoError(t, err)\n\t}\n\n\treturn root, config\n}\n\n// stubs for PFlag Values.\ntype stringValue string\n\nfunc newStringValue(val string, p *string) *stringValue {\n\t*p = val\n\treturn (*stringValue)(p)\n}\n\nfunc (s *stringValue) Set(val string) error {\n\t*s = stringValue(val)\n\treturn nil\n}\n\nfunc (s *stringValue) Type() string {\n\treturn \"string\"\n}\n\nfunc (s *stringValue) String() string {\n\treturn string(*s)\n}\n\nfunc TestGetConfigFile(t *testing.T) {\n\tt.Run(\"config file set\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/etc/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = fs.Create(testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"))\n\t\trequire.NoError(t, err)\n\n\t\tv := New()\n\n\t\tv.SetFs(fs)\n\t\tv.AddConfigPath(\"/etc/viper\")\n\t\tv.SetConfigFile(testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"))\n\n\t\tfilename, err := v.getConfigFile()\n\t\tassert.Equal(t, testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"), filename)\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"find file\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/etc/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = fs.Create(testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"))\n\t\trequire.NoError(t, err)\n\n\t\tv := New()\n\n\t\tv.SetFs(fs)\n\t\tv.AddConfigPath(\"/etc/viper\")\n\n\t\tfilename, err := v.getConfigFile()\n\t\tassert.Equal(t, testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"), filename)\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"find files only\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/etc/config\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = fs.Create(testutil.AbsFilePath(t, \"/etc/config/config.yaml\"))\n\t\trequire.NoError(t, err)\n\n\t\tv := New()\n\n\t\tv.SetFs(fs)\n\t\tv.AddConfigPath(\"/etc\")\n\t\tv.AddConfigPath(\"/etc/config\")\n\n\t\tfilename, err := v.getConfigFile()\n\t\tassert.Equal(t, testutil.AbsFilePath(t, \"/etc/config/config.yaml\"), filename)\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"precedence\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/home/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = fs.Create(testutil.AbsFilePath(t, \"/home/viper/config.zml\"))\n\t\trequire.NoError(t, err)\n\n\t\terr = fs.Mkdir(testutil.AbsFilePath(t, \"/etc/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = fs.Create(testutil.AbsFilePath(t, \"/etc/viper/config.bml\"))\n\t\trequire.NoError(t, err)\n\n\t\terr = fs.Mkdir(testutil.AbsFilePath(t, \"/var/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = fs.Create(testutil.AbsFilePath(t, \"/var/viper/config.yaml\"))\n\t\trequire.NoError(t, err)\n\n\t\tv := New()\n\n\t\tv.SetFs(fs)\n\t\tv.AddConfigPath(\"/home/viper\")\n\t\tv.AddConfigPath(\"/etc/viper\")\n\t\tv.AddConfigPath(\"/var/viper\")\n\n\t\tfilename, err := v.getConfigFile()\n\t\tassert.Equal(t, testutil.AbsFilePath(t, \"/var/viper/config.yaml\"), filename)\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"without extension\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/etc/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = fs.Create(testutil.AbsFilePath(t, \"/etc/viper/.dotfilenoext\"))\n\t\trequire.NoError(t, err)\n\n\t\tv := New()\n\n\t\tv.SetFs(fs)\n\t\tv.AddConfigPath(\"/etc/viper\")\n\t\tv.SetConfigName(\".dotfilenoext\")\n\t\tv.SetConfigType(\"yaml\")\n\n\t\tfilename, err := v.getConfigFile()\n\t\tassert.Equal(t, testutil.AbsFilePath(t, \"/etc/viper/.dotfilenoext\"), filename)\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"without extension and config type\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/etc/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = fs.Create(testutil.AbsFilePath(t, \"/etc/viper/.dotfilenoext\"))\n\t\trequire.NoError(t, err)\n\n\t\tv := New()\n\n\t\tv.SetFs(fs)\n\t\tv.AddConfigPath(\"/etc/viper\")\n\t\tv.SetConfigName(\".dotfilenoext\")\n\n\t\t_, err = v.getConfigFile()\n\t\t// unless config type is set, files without extension\n\t\t// are not considered\n\t\tassert.Error(t, err)\n\t})\n\n\tt.Run(\"experimental finder\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/etc/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = fs.Create(testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"))\n\t\trequire.NoError(t, err)\n\n\t\tv := NewWithOptions(ExperimentalFinder())\n\n\t\tv.SetFs(fs)\n\n\t\tv.AddConfigPath(\"/etc/viper\")\n\n\t\tfilename, err := v.getConfigFile()\n\t\tassert.Equal(t, testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"), testutil.AbsFilePath(t, filename))\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"finder\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/etc/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = fs.Create(testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"))\n\t\trequire.NoError(t, err)\n\n\t\tfinder := locafero.Finder{\n\t\t\tPaths: []string{testutil.AbsFilePath(t, \"/etc/viper\")},\n\t\t\tNames: locafero.NameWithExtensions(\"config\", SupportedExts...),\n\t\t\tType:  locafero.FileTypeFile,\n\t\t}\n\n\t\tv := NewWithOptions(WithFinder(finder))\n\n\t\tv.SetFs(fs)\n\n\t\t// These should be ineffective\n\t\tv.AddConfigPath(\"/etc/something_else\")\n\t\tv.SetConfigName(\"not-config\")\n\n\t\tfilename, err := v.getConfigFile()\n\t\tassert.Equal(t, testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"), testutil.AbsFilePath(t, filename))\n\t\tassert.NoError(t, err)\n\t})\n}\n\nfunc TestReadInConfig(t *testing.T) {\n\tt.Run(\"config file set\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/etc/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\tfile, err := fs.Create(testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"))\n\t\trequire.NoError(t, err)\n\n\t\t_, err = file.WriteString(`key: value`)\n\t\trequire.NoError(t, err)\n\n\t\tfile.Close()\n\n\t\tv := New()\n\n\t\tv.SetFs(fs)\n\t\tv.SetConfigFile(testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"))\n\n\t\terr = v.ReadInConfig()\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, \"value\", v.Get(\"key\"))\n\t})\n\n\tt.Run(\"find file\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/etc/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\tfile, err := fs.Create(testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"))\n\t\trequire.NoError(t, err)\n\n\t\t_, err = file.WriteString(`key: value`)\n\t\trequire.NoError(t, err)\n\n\t\tfile.Close()\n\n\t\tv := New()\n\n\t\tv.SetFs(fs)\n\t\tv.AddConfigPath(\"/etc/viper\")\n\n\t\terr = v.ReadInConfig()\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, \"value\", v.Get(\"key\"))\n\t})\n\n\tt.Run(\"find file with experimental finder\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/etc/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\tfile, err := fs.Create(testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"))\n\t\trequire.NoError(t, err)\n\n\t\t_, err = file.WriteString(`key: value`)\n\t\trequire.NoError(t, err)\n\n\t\tfile.Close()\n\n\t\tv := NewWithOptions(ExperimentalFinder())\n\n\t\tv.SetFs(fs)\n\t\tv.AddConfigPath(\"/etc/viper\")\n\n\t\terr = v.ReadInConfig()\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, \"value\", v.Get(\"key\"))\n\t})\n\n\tt.Run(\"find file using a finder\", func(t *testing.T) {\n\t\tfs := afero.NewMemMapFs()\n\n\t\terr := fs.Mkdir(testutil.AbsFilePath(t, \"/etc/viper\"), 0o777)\n\t\trequire.NoError(t, err)\n\n\t\tfile, err := fs.Create(testutil.AbsFilePath(t, \"/etc/viper/config.yaml\"))\n\t\trequire.NoError(t, err)\n\n\t\t_, err = file.WriteString(`key: value`)\n\t\trequire.NoError(t, err)\n\n\t\tfile.Close()\n\n\t\tfinder := locafero.Finder{\n\t\t\tPaths: []string{testutil.AbsFilePath(t, \"/etc/viper\")},\n\t\t\tNames: locafero.NameWithExtensions(\"config\", SupportedExts...),\n\t\t\tType:  locafero.FileTypeFile,\n\t\t}\n\n\t\tv := NewWithOptions(WithFinder(finder))\n\n\t\tv.SetFs(fs)\n\n\t\t// These should be ineffective\n\t\tv.AddConfigPath(\"/etc/something_else\")\n\t\tv.SetConfigName(\"not-config\")\n\n\t\terr = v.ReadInConfig()\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equal(t, \"value\", v.Get(\"key\"))\n\t})\n}\n\nfunc TestDefault(t *testing.T) {\n\tv := New()\n\tv.SetDefault(\"age\", 45)\n\tassert.Equal(t, 45, v.Get(\"age\"))\n\n\tv.SetDefault(\"clothing.jacket\", \"slacks\")\n\tassert.Equal(t, \"slacks\", v.Get(\"clothing.jacket\"))\n\n\tv.SetConfigType(\"yaml\")\n\terr := v.ReadConfig(bytes.NewBuffer(yamlExample))\n\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"leather\", v.Get(\"clothing.jacket\"))\n}\n\nfunc TestUnmarshaling(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"yaml\")\n\tr := bytes.NewReader(yamlExample)\n\n\tv.unmarshalReader(r, v.config)\n\tassert.True(t, v.InConfig(\"name\"))\n\tassert.True(t, v.InConfig(\"clothing.jacket\"))\n\tassert.False(t, v.InConfig(\"state\"))\n\tassert.False(t, v.InConfig(\"clothing.hat\"))\n\tassert.Equal(t, \"steve\", v.Get(\"name\"))\n\tassert.Equal(t, []any{\"skateboarding\", \"snowboarding\", \"go\"}, v.Get(\"hobbies\"))\n\tassert.Equal(t, map[string]any{\"jacket\": \"leather\", \"trousers\": \"denim\", \"pants\": map[string]any{\"size\": \"large\"}}, v.Get(\"clothing\"))\n\tassert.Equal(t, 35, v.Get(\"age\"))\n}\n\nfunc TestUnmarshalExact(t *testing.T) {\n\tv := New()\n\ttarget := &testUnmarshalExtra{}\n\tv.SetConfigType(\"yaml\")\n\tr := bytes.NewReader(yamlExampleWithExtras)\n\tv.ReadConfig(r)\n\terr := v.UnmarshalExact(target)\n\tassert.Error(t, err, \"UnmarshalExact should error when populating a struct from a conf that contains unused fields\")\n}\n\nfunc TestOverrides(t *testing.T) {\n\tv := New()\n\tv.Set(\"age\", 40)\n\tassert.Equal(t, 40, v.Get(\"age\"))\n}\n\nfunc TestDefaultPost(t *testing.T) {\n\tv := New()\n\tassert.NotEqual(t, \"NYC\", v.Get(\"state\"))\n\tv.SetDefault(\"state\", \"NYC\")\n\tassert.Equal(t, \"NYC\", v.Get(\"state\"))\n}\n\nfunc TestAliases(t *testing.T) {\n\tv := New()\n\tv.Set(\"age\", 40)\n\tv.RegisterAlias(\"years\", \"age\")\n\tassert.Equal(t, 40, v.Get(\"years\"))\n\tv.Set(\"years\", 45)\n\tassert.Equal(t, 45, v.Get(\"age\"))\n}\n\nfunc TestAliasInConfigFile(t *testing.T) {\n\tv := New()\n\n\tv.SetConfigType(\"yaml\")\n\n\t// Read the YAML data into Viper configuration\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)), \"Error reading YAML data\")\n\n\tv.RegisterAlias(\"beard\", \"hasbeard\")\n\tassert.Equal(t, true, v.Get(\"hasbeard\"))\n\n\tv.Set(\"hasbeard\", false)\n\tassert.Equal(t, false, v.Get(\"beard\"))\n}\n\nfunc TestYML(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"yaml\")\n\n\t// Read the YAML data into Viper configuration\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)), \"Error reading YAML data\")\n\n\tassert.Equal(t, \"steve\", v.Get(\"name\"))\n}\n\nfunc TestJSON(t *testing.T) {\n\tv := New()\n\n\tv.SetConfigType(\"json\")\n\t// Read the JSON data into Viper configuration\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), \"Error reading JSON data\")\n\n\tassert.Equal(t, \"0001\", v.Get(\"id\"))\n}\n\nfunc TestTOML(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"toml\")\n\n\t// Read the TOML data into Viper configuration\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(tomlExample)), \"Error reading toml data\")\n\n\tassert.Equal(t, \"TOML Example\", v.Get(\"title\"))\n}\n\nfunc TestDotEnv(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"env\")\n\t// Read the dotenv data into Viper configuration\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(dotenvExample)), \"Error reading env data\")\n\n\tassert.Equal(t, \"DotEnv Example\", v.Get(\"title_dotenv\"))\n}\n\nfunc TestRemotePrecedence(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"json\")\n\t// Read the remote data into Viper configuration v.config\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), \"Error reading json data\")\n\n\tassert.Equal(t, \"0001\", v.Get(\"id\"))\n\n\t// update the kvstore with the remoteExample which should overite the key in v.config\n\tremote := bytes.NewReader(remoteExample)\n\trequire.NoError(t, v.unmarshalReader(remote, v.kvstore), \"Error reading json data in to kvstore\")\n\n\tassert.Equal(t, \"0001\", v.Get(\"id\"))\n\tassert.NotEqual(t, \"cronut\", v.Get(\"type\"))\n\tassert.Equal(t, \"remote\", v.Get(\"newkey\"))\n\tv.Set(\"newkey\", \"newvalue\")\n\tassert.NotEqual(t, \"remote\", v.Get(\"newkey\"))\n\tassert.Equal(t, \"newvalue\", v.Get(\"newkey\"))\n}\n\nfunc TestEnv(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"json\")\n\t// Read the JSON data into Viper configuration v.config\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), \"Error reading json data\")\n\n\tv.BindEnv(\"id\")\n\tv.BindEnv(\"f\", \"FOOD\", \"OLD_FOOD\")\n\n\tt.Setenv(\"ID\", \"13\")\n\tt.Setenv(\"FOOD\", \"apple\")\n\tt.Setenv(\"OLD_FOOD\", \"banana\")\n\tt.Setenv(\"NAME\", \"crunk\")\n\n\tassert.Equal(t, \"13\", v.Get(\"id\"))\n\tassert.Equal(t, \"apple\", v.Get(\"f\"))\n\tassert.Equal(t, \"Cake\", v.Get(\"name\"))\n\n\tv.AutomaticEnv()\n\n\tassert.Equal(t, \"crunk\", v.Get(\"name\"))\n}\n\nfunc TestMultipleEnv(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"json\")\n\t// Read the JSON data into Viper configuration v.config\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), \"Error reading json data\")\n\n\tv.BindEnv(\"f\", \"FOOD\", \"OLD_FOOD\")\n\n\tt.Setenv(\"OLD_FOOD\", \"banana\")\n\n\tassert.Equal(t, \"banana\", v.Get(\"f\"))\n}\n\nfunc TestEmptyEnv(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"json\")\n\t// Read the JSON data into Viper configuration v.config\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), \"Error reading json data\")\n\n\tv.BindEnv(\"type\") // Empty environment variable\n\tv.BindEnv(\"name\") // Bound, but not set environment variable\n\n\tt.Setenv(\"TYPE\", \"\")\n\n\tassert.Equal(t, \"donut\", v.Get(\"type\"))\n\tassert.Equal(t, \"Cake\", v.Get(\"name\"))\n}\n\nfunc TestEmptyEnv_Allowed(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"json\")\n\t// Read the JSON data into Viper configuration v.config\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), \"Error reading json data\")\n\n\tv.AllowEmptyEnv(true)\n\n\tv.BindEnv(\"type\") // Empty environment variable\n\tv.BindEnv(\"name\") // Bound, but not set environment variable\n\n\tt.Setenv(\"TYPE\", \"\")\n\n\tassert.Equal(t, \"\", v.Get(\"type\"))\n\tassert.Equal(t, \"Cake\", v.Get(\"name\"))\n}\n\nfunc TestEnvPrefix(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"json\")\n\t// Read the JSON data into Viper configuration v.config\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), \"Error reading json data\")\n\n\tv.SetEnvPrefix(\"foo\") // will be uppercased automatically\n\tv.BindEnv(\"id\")\n\tv.BindEnv(\"f\", \"FOOD\") // not using prefix\n\n\tt.Setenv(\"FOO_ID\", \"13\")\n\tt.Setenv(\"FOOD\", \"apple\")\n\tt.Setenv(\"FOO_NAME\", \"crunk\")\n\n\tassert.Equal(t, \"13\", v.Get(\"id\"))\n\tassert.Equal(t, \"apple\", v.Get(\"f\"))\n\tassert.Equal(t, \"Cake\", v.Get(\"name\"))\n\n\tv.AutomaticEnv()\n\n\tassert.Equal(t, \"crunk\", v.Get(\"name\"))\n}\n\nfunc TestAutoEnv(t *testing.T) {\n\tv := New()\n\n\tv.AutomaticEnv()\n\n\tt.Setenv(\"FOO_BAR\", \"13\")\n\n\tassert.Equal(t, \"13\", v.Get(\"foo_bar\"))\n}\n\nfunc TestAutoEnvWithPrefix(t *testing.T) {\n\tv := New()\n\tv.AutomaticEnv()\n\tv.SetEnvPrefix(\"Baz\")\n\tt.Setenv(\"BAZ_BAR\", \"13\")\n\tassert.Equal(t, \"13\", v.Get(\"bar\"))\n}\n\nfunc TestSetEnvKeyReplacer(t *testing.T) {\n\tv := New()\n\tv.AutomaticEnv()\n\n\tt.Setenv(\"REFRESH_INTERVAL\", \"30s\")\n\n\treplacer := strings.NewReplacer(\"-\", \"_\")\n\tv.SetEnvKeyReplacer(replacer)\n\n\tassert.Equal(t, \"30s\", v.Get(\"refresh-interval\"))\n}\n\nfunc TestEnvKeyReplacer(t *testing.T) {\n\tv := NewWithOptions(EnvKeyReplacer(strings.NewReplacer(\"-\", \"_\")))\n\tv.AutomaticEnv()\n\tt.Setenv(\"REFRESH_INTERVAL\", \"30s\")\n\tassert.Equal(t, \"30s\", v.Get(\"refresh-interval\"))\n}\n\nfunc TestEnvSubConfig(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"yaml\")\n\t// Read the YAML data into Viper configuration v.config\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)), \"Error reading json data\")\n\tv.AutomaticEnv()\n\tv.SetEnvKeyReplacer(strings.NewReplacer(\".\", \"_\"))\n\n\tt.Setenv(\"CLOTHING_PANTS_SIZE\", \"small\")\n\tsubv := v.Sub(\"clothing\").Sub(\"pants\")\n\tassert.Equal(t, \"small\", subv.Get(\"size\"))\n\n\t// again with EnvPrefix\n\tv.SetEnvPrefix(\"foo\") // will be uppercased automatically\n\tsubWithPrefix := v.Sub(\"clothing\").Sub(\"pants\")\n\tt.Setenv(\"FOO_CLOTHING_PANTS_SIZE\", \"large\")\n\tassert.Equal(t, \"large\", subWithPrefix.Get(\"size\"))\n}\n\nfunc TestAllKeys(t *testing.T) {\n\tv := New()\n\tinitConfigs(v)\n\n\tks := []string{\n\t\t\"title\",\n\t\t\"newkey\",\n\t\t\"owner.organization\",\n\t\t\"owner.dob\",\n\t\t\"owner.bio\",\n\t\t\"name\",\n\t\t\"beard\",\n\t\t\"ppu\",\n\t\t\"batters.batter\",\n\t\t\"hobbies\",\n\t\t\"clothing.jacket\",\n\t\t\"clothing.trousers\",\n\t\t\"clothing.pants.size\",\n\t\t\"age\",\n\t\t\"hacker\",\n\t\t\"id\",\n\t\t\"type\",\n\t\t\"eyes\",\n\t\t\"title_dotenv\",\n\t\t\"type_dotenv\",\n\t\t\"name_dotenv\",\n\t}\n\tdob, _ := time.Parse(time.RFC3339, \"1979-05-27T07:32:00Z\")\n\tall := map[string]any{\n\t\t\"owner\": map[string]any{\n\t\t\t\"organization\": \"MongoDB\",\n\t\t\t\"bio\":          \"MongoDB Chief Developer Advocate & Hacker at Large\",\n\t\t\t\"dob\":          dob,\n\t\t},\n\t\t\"title\": \"TOML Example\",\n\t\t\"ppu\":   0.55,\n\t\t\"eyes\":  \"brown\",\n\t\t\"clothing\": map[string]any{\n\t\t\t\"trousers\": \"denim\",\n\t\t\t\"jacket\":   \"leather\",\n\t\t\t\"pants\":    map[string]any{\"size\": \"large\"},\n\t\t},\n\t\t\"id\": \"0001\",\n\t\t\"batters\": map[string]any{\n\t\t\t\"batter\": []any{\n\t\t\t\tmap[string]any{\"type\": \"Regular\"},\n\t\t\t\tmap[string]any{\"type\": \"Chocolate\"},\n\t\t\t\tmap[string]any{\"type\": \"Blueberry\"},\n\t\t\t\tmap[string]any{\"type\": \"Devil's Food\"},\n\t\t\t},\n\t\t},\n\t\t\"hacker\": true,\n\t\t\"beard\":  true,\n\t\t\"hobbies\": []any{\n\t\t\t\"skateboarding\",\n\t\t\t\"snowboarding\",\n\t\t\t\"go\",\n\t\t},\n\t\t\"age\":          35,\n\t\t\"type\":         \"donut\",\n\t\t\"newkey\":       \"remote\",\n\t\t\"name\":         \"Cake\",\n\t\t\"title_dotenv\": \"DotEnv Example\",\n\t\t\"type_dotenv\":  \"donut\",\n\t\t\"name_dotenv\":  \"Cake\",\n\t}\n\n\tassert.ElementsMatch(t, ks, v.AllKeys())\n\tassert.Equal(t, all, v.AllSettings())\n}\n\nfunc TestAllKeysWithEnv(t *testing.T) {\n\tv := New()\n\n\t// bind and define environment variables (including a nested one)\n\tv.BindEnv(\"id\")\n\tv.BindEnv(\"foo.bar\")\n\tv.SetEnvKeyReplacer(strings.NewReplacer(\".\", \"_\"))\n\n\tt.Setenv(\"ID\", \"13\")\n\tt.Setenv(\"FOO_BAR\", \"baz\")\n\n\tassert.ElementsMatch(t, []string{\"id\", \"foo.bar\"}, v.AllKeys())\n}\n\nfunc TestAliasesOfAliases(t *testing.T) {\n\tv := New()\n\tv.Set(\"Title\", \"Checking Case\")\n\tv.RegisterAlias(\"Foo\", \"Bar\")\n\tv.RegisterAlias(\"Bar\", \"Title\")\n\tassert.Equal(t, \"Checking Case\", v.Get(\"FOO\"))\n}\n\nfunc TestRecursiveAliases(t *testing.T) {\n\tv := New()\n\tv.Set(\"baz\", \"bat\")\n\tv.RegisterAlias(\"Baz\", \"Roo\")\n\tv.RegisterAlias(\"Roo\", \"baz\")\n\tassert.Equal(t, \"bat\", v.Get(\"Baz\"))\n}\n\nfunc TestUnmarshal(t *testing.T) {\n\tv := New()\n\tv.SetDefault(\"port\", 1313)\n\tv.Set(\"name\", \"Steve\")\n\tv.Set(\"duration\", \"1s1ms\")\n\tv.Set(\"modes\", []int{1, 2, 3})\n\n\ttype config struct {\n\t\tPort     int\n\t\tName     string\n\t\tDuration time.Duration\n\t\tModes    []int\n\t}\n\n\tvar C config\n\trequire.NoError(t, v.Unmarshal(&C), \"unable to decode into struct\")\n\n\tassert.Equal(\n\t\tt,\n\t\t&config{\n\t\t\tName:     \"Steve\",\n\t\t\tPort:     1313,\n\t\t\tDuration: time.Second + time.Millisecond,\n\t\t\tModes:    []int{1, 2, 3},\n\t\t},\n\t\t&C,\n\t)\n\n\tv.Set(\"port\", 1234)\n\trequire.NoError(t, v.Unmarshal(&C), \"unable to decode into struct\")\n\n\tassert.Equal(\n\t\tt,\n\t\t&config{\n\t\t\tName:     \"Steve\",\n\t\t\tPort:     1234,\n\t\t\tDuration: time.Second + time.Millisecond,\n\t\t\tModes:    []int{1, 2, 3},\n\t\t},\n\t\t&C,\n\t)\n}\n\nfunc TestUnmarshalWithDefaultDecodeHook(t *testing.T) {\n\topt := mapstructure.ComposeDecodeHookFunc(\n\t\tmapstructure.StringToTimeDurationHookFunc(),\n\t\tmapstructure.StringToSliceHookFunc(\",\"),\n\t\t// Custom Decode Hook Function\n\t\tfunc(rf reflect.Kind, rt reflect.Kind, data any) (any, error) {\n\t\t\tif rf != reflect.String || rt != reflect.Map {\n\t\t\t\treturn data, nil\n\t\t\t}\n\t\t\tm := map[string]string{}\n\t\t\traw := data.(string)\n\t\t\tif raw == \"\" {\n\t\t\t\treturn m, nil\n\t\t\t}\n\t\t\terr := json.Unmarshal([]byte(raw), &m)\n\t\t\treturn m, err\n\t\t},\n\t)\n\n\tv := NewWithOptions(WithDecodeHook(opt))\n\tv.Set(\"credentials\", \"{\\\"foo\\\":\\\"bar\\\"}\")\n\n\ttype config struct {\n\t\tCredentials map[string]string\n\t}\n\n\tvar C config\n\n\trequire.NoError(t, v.Unmarshal(&C), \"unable to decode into struct\")\n\n\tassert.Equal(t, &config{\n\t\tCredentials: map[string]string{\"foo\": \"bar\"},\n\t}, &C)\n}\n\nfunc TestUnmarshalWithDecoderOptions(t *testing.T) {\n\tv := New()\n\tv.Set(\"credentials\", \"{\\\"foo\\\":\\\"bar\\\"}\")\n\n\topt := DecodeHook(mapstructure.ComposeDecodeHookFunc(\n\t\tmapstructure.StringToTimeDurationHookFunc(),\n\t\tmapstructure.StringToSliceHookFunc(\",\"),\n\t\t// Custom Decode Hook Function\n\t\tfunc(rf reflect.Kind, rt reflect.Kind, data any) (any, error) {\n\t\t\tif rf != reflect.String || rt != reflect.Map {\n\t\t\t\treturn data, nil\n\t\t\t}\n\t\t\tm := map[string]string{}\n\t\t\traw := data.(string)\n\t\t\tif raw == \"\" {\n\t\t\t\treturn m, nil\n\t\t\t}\n\t\t\terr := json.Unmarshal([]byte(raw), &m)\n\t\t\treturn m, err\n\t\t},\n\t))\n\n\ttype config struct {\n\t\tCredentials map[string]string\n\t}\n\n\tvar C config\n\n\trequire.NoError(t, v.Unmarshal(&C, opt), \"unable to decode into struct\")\n\n\tassert.Equal(t, &config{\n\t\tCredentials: map[string]string{\"foo\": \"bar\"},\n\t}, &C)\n}\n\nfunc TestUnmarshalWithAutomaticEnv(t *testing.T) {\n\tt.Setenv(\"PORT\", \"1313\")\n\tt.Setenv(\"NAME\", \"Steve\")\n\tt.Setenv(\"DURATION\", \"1s1ms\")\n\tt.Setenv(\"MODES\", \"1,2,3\")\n\tt.Setenv(\"SECRET\", \"42\")\n\tt.Setenv(\"FILESYSTEM_SIZE\", \"4096\")\n\n\ttype AuthConfig struct {\n\t\tSecret string `mapstructure:\"secret\"`\n\t}\n\n\ttype StorageConfig struct {\n\t\tSize int `mapstructure:\"size\"`\n\t}\n\n\ttype Configuration struct {\n\t\tPort     int           `mapstructure:\"port\"`\n\t\tName     string        `mapstructure:\"name\"`\n\t\tDuration time.Duration `mapstructure:\"duration\"`\n\n\t\t// Infer name from struct\n\t\tModes []int\n\n\t\t// Squash nested struct (omit prefix)\n\t\tAuthentication AuthConfig `mapstructure:\",squash\"`\n\n\t\t// Different key\n\t\tStorage StorageConfig `mapstructure:\"filesystem\"`\n\n\t\t// Omitted field\n\t\tFlag bool `mapstructure:\"flag\"`\n\t}\n\n\tv := NewWithOptions(ExperimentalBindStruct())\n\tv.SetEnvKeyReplacer(strings.NewReplacer(\".\", \"_\"))\n\tv.AutomaticEnv()\n\n\tt.Run(\"OK\", func(t *testing.T) {\n\t\tvar config Configuration\n\t\tif err := v.Unmarshal(&config); err != nil {\n\t\t\tt.Fatalf(\"unable to decode into struct, %v\", err)\n\t\t}\n\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\tConfiguration{\n\t\t\t\tName:     \"Steve\",\n\t\t\t\tPort:     1313,\n\t\t\t\tDuration: time.Second + time.Millisecond,\n\t\t\t\tModes:    []int{1, 2, 3},\n\t\t\t\tAuthentication: AuthConfig{\n\t\t\t\t\tSecret: \"42\",\n\t\t\t\t},\n\t\t\t\tStorage: StorageConfig{\n\t\t\t\t\tSize: 4096,\n\t\t\t\t},\n\t\t\t},\n\t\t\tconfig,\n\t\t)\n\t})\n\n\tt.Run(\"Precedence\", func(t *testing.T) {\n\t\tvar config Configuration\n\n\t\tv.Set(\"port\", 1234)\n\t\tif err := v.Unmarshal(&config); err != nil {\n\t\t\tt.Fatalf(\"unable to decode into struct, %v\", err)\n\t\t}\n\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\tConfiguration{\n\t\t\t\tName:     \"Steve\",\n\t\t\t\tPort:     1234,\n\t\t\t\tDuration: time.Second + time.Millisecond,\n\t\t\t\tModes:    []int{1, 2, 3},\n\t\t\t\tAuthentication: AuthConfig{\n\t\t\t\t\tSecret: \"42\",\n\t\t\t\t},\n\t\t\t\tStorage: StorageConfig{\n\t\t\t\t\tSize: 4096,\n\t\t\t\t},\n\t\t\t},\n\t\t\tconfig,\n\t\t)\n\t})\n\n\tt.Run(\"Unset\", func(t *testing.T) {\n\t\tvar config Configuration\n\n\t\terr := v.Unmarshal(&config, func(config *mapstructure.DecoderConfig) {\n\t\t\tconfig.ErrorUnset = true\n\t\t})\n\n\t\tassert.Error(t, err, \"expected viper.Unmarshal to return error due to unset field 'FLAG'\")\n\t})\n\n\tt.Run(\"Exact\", func(t *testing.T) {\n\t\tvar config Configuration\n\n\t\tv.Set(\"port\", 1234)\n\t\tif err := v.UnmarshalExact(&config); err != nil {\n\t\t\tt.Fatalf(\"unable to decode into struct, %v\", err)\n\t\t}\n\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\tConfiguration{\n\t\t\t\tName:     \"Steve\",\n\t\t\t\tPort:     1234,\n\t\t\t\tDuration: time.Second + time.Millisecond,\n\t\t\t\tModes:    []int{1, 2, 3},\n\t\t\t\tAuthentication: AuthConfig{\n\t\t\t\t\tSecret: \"42\",\n\t\t\t\t},\n\t\t\t\tStorage: StorageConfig{\n\t\t\t\t\tSize: 4096,\n\t\t\t\t},\n\t\t\t},\n\t\t\tconfig,\n\t\t)\n\t})\n}\n\nfunc TestBindPFlags(t *testing.T) {\n\tv := New() // create independent Viper object\n\tflagSet := pflag.NewFlagSet(\"test\", pflag.ContinueOnError)\n\n\ttestValues := map[string]*string{\n\t\t\"host\":     nil,\n\t\t\"port\":     nil,\n\t\t\"endpoint\": nil,\n\t}\n\n\tmutatedTestValues := map[string]string{\n\t\t\"host\":     \"localhost\",\n\t\t\"port\":     \"6060\",\n\t\t\"endpoint\": \"/public\",\n\t}\n\n\tfor name := range testValues {\n\t\ttestValues[name] = flagSet.String(name, \"\", \"test\")\n\t}\n\n\terr := v.BindPFlags(flagSet)\n\trequire.NoError(t, err, \"error binding flag set\")\n\n\tflagSet.VisitAll(func(flag *pflag.Flag) {\n\t\tflag.Value.Set(mutatedTestValues[flag.Name])\n\t\tflag.Changed = true\n\t})\n\n\tfor name, expected := range mutatedTestValues {\n\t\tassert.Equal(t, expected, v.Get(name))\n\t}\n}\n\nfunc TestBindPFlagsStringSlice(t *testing.T) {\n\ttests := []struct {\n\t\tExpected []string\n\t\tValue    string\n\t}{\n\t\t{[]string{}, \"\"},\n\t\t{[]string{\"jeden\"}, \"jeden\"},\n\t\t{[]string{\"dwa\", \"trzy\"}, \"dwa,trzy\"},\n\t\t{[]string{\"cztery\", \"piec , szesc\"}, \"cztery,\\\"piec , szesc\\\"\"},\n\t}\n\n\tv := New() // create independent Viper object\n\tdefaultVal := []string{\"default\"}\n\tv.SetDefault(\"stringslice\", defaultVal)\n\n\tfor _, testValue := range tests {\n\t\tflagSet := pflag.NewFlagSet(\"test\", pflag.ContinueOnError)\n\t\tflagSet.StringSlice(\"stringslice\", testValue.Expected, \"test\")\n\n\t\tfor _, changed := range []bool{true, false} {\n\t\t\tflagSet.VisitAll(func(f *pflag.Flag) {\n\t\t\t\tf.Value.Set(testValue.Value)\n\t\t\t\tf.Changed = changed\n\t\t\t})\n\n\t\t\terr := v.BindPFlags(flagSet)\n\t\t\trequire.NoError(t, err, \"error binding flag set\")\n\n\t\t\ttype TestStr struct {\n\t\t\t\tStringSlice []string\n\t\t\t}\n\t\t\tval := &TestStr{}\n\t\t\terr = v.Unmarshal(val)\n\t\t\trequire.NoError(t, err, \"cannot unmarshal\")\n\t\t\tif changed {\n\t\t\t\tassert.Equal(t, testValue.Expected, val.StringSlice)\n\t\t\t\tassert.Equal(t, testValue.Expected, v.Get(\"stringslice\"))\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, defaultVal, val.StringSlice)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestBindPFlagsStringArray(t *testing.T) {\n\ttests := []struct {\n\t\tExpected []string\n\t\tValue    string\n\t}{\n\t\t{[]string{}, \"\"},\n\t\t{[]string{\"jeden\"}, \"jeden\"},\n\t\t{[]string{\"dwa,trzy\"}, \"dwa,trzy\"},\n\t\t{[]string{\"cztery,\\\"piec , szesc\\\"\"}, \"cztery,\\\"piec , szesc\\\"\"},\n\t}\n\n\tv := New() // create independent Viper object\n\tdefaultVal := []string{\"default\"}\n\tv.SetDefault(\"stringarray\", defaultVal)\n\n\tfor _, testValue := range tests {\n\t\tflagSet := pflag.NewFlagSet(\"test\", pflag.ContinueOnError)\n\t\tflagSet.StringArray(\"stringarray\", testValue.Expected, \"test\")\n\n\t\tfor _, changed := range []bool{true, false} {\n\t\t\tflagSet.VisitAll(func(f *pflag.Flag) {\n\t\t\t\tf.Value.Set(testValue.Value)\n\t\t\t\tf.Changed = changed\n\t\t\t})\n\n\t\t\terr := v.BindPFlags(flagSet)\n\t\t\trequire.NoError(t, err, \"error binding flag set\")\n\n\t\t\ttype TestStr struct {\n\t\t\t\tStringArray []string\n\t\t\t}\n\t\t\tval := &TestStr{}\n\t\t\terr = v.Unmarshal(val)\n\t\t\trequire.NoError(t, err, \"cannot unmarshal\")\n\t\t\tif changed {\n\t\t\t\tassert.Equal(t, testValue.Expected, val.StringArray)\n\t\t\t\tassert.Equal(t, testValue.Expected, v.Get(\"stringarray\"))\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, defaultVal, val.StringArray)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestBindPFlagsSlices(t *testing.T) {\n\tset := pflag.NewFlagSet(\"test\", pflag.ContinueOnError)\n\tset.IntSlice(\"intslice\", []int{}, \"\")\n\tset.BoolSlice(\"boolslice\", []bool{}, \"\")\n\tset.Float64Slice(\"float64slice\", []float64{}, \"\")\n\tset.UintSlice(\"uintslice\", []uint{}, \"\")\n\n\tv := New()\n\tv.BindPFlags(set)\n\n\tset.Set(\"intslice\", \"1,2\")\n\tassert.Equal(t, []int{1, 2}, v.Get(\"intslice\"))\n\n\tset.Set(\"boolslice\", \"true,false\")\n\tassert.Equal(t, []bool{true, false}, v.Get(\"boolslice\"))\n\n\tset.Set(\"float64slice\", \"1.1,2.2\")\n\tassert.Equal(t, []float64{1.1, 2.2}, v.Get(\"float64slice\"))\n\n\tset.Set(\"uintslice\", \"1,2\")\n\tassert.Equal(t, []uint{1, 2}, v.Get(\"uintslice\"))\n}\n\nfunc TestSliceFlagsReturnCorrectType(t *testing.T) {\n\tflagSet := pflag.NewFlagSet(\"test\", pflag.ContinueOnError)\n\tflagSet.IntSlice(\"int\", []int{1, 2}, \"\")\n\tflagSet.StringSlice(\"str\", []string{\"3\", \"4\"}, \"\")\n\tflagSet.DurationSlice(\"duration\", []time.Duration{5 * time.Second}, \"\")\n\n\tv := New()\n\tv.BindPFlags(flagSet)\n\n\tall := v.AllSettings()\n\n\tassert.IsType(t, []int{}, all[\"int\"])\n\tassert.IsType(t, []string{}, all[\"str\"])\n\tassert.IsType(t, []time.Duration{}, all[\"duration\"])\n}\n\nfunc TestBindPFlagsIntSlice(t *testing.T) {\n\ttests := []struct {\n\t\tExpected []int\n\t\tValue    string\n\t}{\n\t\t{[]int{}, \"\"},\n\t\t{[]int{1}, \"1\"},\n\t\t{[]int{2, 3}, \"2,3\"},\n\t}\n\n\tv := New() // create independent Viper object\n\tdefaultVal := []int{0}\n\tv.SetDefault(\"intslice\", defaultVal)\n\n\tfor _, testValue := range tests {\n\t\tflagSet := pflag.NewFlagSet(\"test\", pflag.ContinueOnError)\n\t\tflagSet.IntSlice(\"intslice\", testValue.Expected, \"test\")\n\n\t\tfor _, changed := range []bool{true, false} {\n\t\t\tflagSet.VisitAll(func(f *pflag.Flag) {\n\t\t\t\tf.Value.Set(testValue.Value)\n\t\t\t\tf.Changed = changed\n\t\t\t})\n\n\t\t\terr := v.BindPFlags(flagSet)\n\t\t\trequire.NoError(t, err, \"error binding flag set\")\n\n\t\t\ttype TestInt struct {\n\t\t\t\tIntSlice []int\n\t\t\t}\n\t\t\tval := &TestInt{}\n\t\t\terr = v.Unmarshal(val)\n\t\t\trequire.NoError(t, err, \"cannot unmarshal\")\n\t\t\tif changed {\n\t\t\t\tassert.Equal(t, testValue.Expected, val.IntSlice)\n\t\t\t\tassert.Equal(t, testValue.Expected, v.Get(\"intslice\"))\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, defaultVal, val.IntSlice)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestBindPFlag(t *testing.T) {\n\tv := New()\n\ttestString := \"testing\"\n\ttestValue := newStringValue(testString, &testString)\n\n\tflag := &pflag.Flag{\n\t\tName:    \"testflag\",\n\t\tValue:   testValue,\n\t\tChanged: false,\n\t}\n\n\tv.BindPFlag(\"testvalue\", flag)\n\n\tassert.Equal(t, testString, v.Get(\"testvalue\"))\n\n\tflag.Value.Set(\"testing_mutate\")\n\tflag.Changed = true // hack for pflag usage\n\n\tassert.Equal(t, \"testing_mutate\", v.Get(\"testvalue\"))\n}\n\nfunc TestBindPFlagDetectNilFlag(t *testing.T) {\n\tv := New()\n\tresult := v.BindPFlag(\"testvalue\", nil)\n\tassert.Error(t, result)\n}\n\nfunc TestBindPFlagStringToString(t *testing.T) {\n\ttests := []struct {\n\t\tExpected map[string]string\n\t\tValue    string\n\t}{\n\t\t{map[string]string{}, \"\"},\n\t\t{map[string]string{\"yo\": \"hi\"}, \"yo=hi\"},\n\t\t{map[string]string{\"yo\": \"hi\", \"oh\": \"hi=there\"}, \"yo=hi,oh=hi=there\"},\n\t\t{map[string]string{\"yo\": \"\"}, \"yo=\"},\n\t\t{map[string]string{\"yo\": \"\", \"oh\": \"hi=there\"}, \"yo=,oh=hi=there\"},\n\t}\n\n\tv := New() // create independent Viper object\n\tdefaultVal := map[string]string{}\n\tv.SetDefault(\"stringtostring\", defaultVal)\n\n\tfor _, testValue := range tests {\n\t\tflagSet := pflag.NewFlagSet(\"test\", pflag.ContinueOnError)\n\t\tflagSet.StringToString(\"stringtostring\", testValue.Expected, \"test\")\n\n\t\tfor _, changed := range []bool{true, false} {\n\t\t\tflagSet.VisitAll(func(f *pflag.Flag) {\n\t\t\t\tf.Value.Set(testValue.Value)\n\t\t\t\tf.Changed = changed\n\t\t\t})\n\n\t\t\terr := v.BindPFlags(flagSet)\n\t\t\trequire.NoError(t, err, \"error binding flag set\")\n\n\t\t\ttype TestMap struct {\n\t\t\t\tStringToString map[string]string\n\t\t\t}\n\t\t\tval := &TestMap{}\n\t\t\terr = v.Unmarshal(val)\n\t\t\trequire.NoError(t, err, \"cannot unmarshal\")\n\t\t\tif changed {\n\t\t\t\tassert.Equal(t, testValue.Expected, val.StringToString)\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, defaultVal, val.StringToString)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestBindPFlagStringToInt(t *testing.T) {\n\ttests := []struct {\n\t\tExpected map[string]int\n\t\tValue    string\n\t}{\n\t\t{map[string]int{\"yo\": 1, \"oh\": 21}, \"yo=1,oh=21\"},\n\t\t{map[string]int{\"yo\": 100000000, \"oh\": 0}, \"yo=100000000,oh=0\"},\n\t\t{map[string]int{}, \"yo=2,oh=21.0\"},\n\t\t{map[string]int{}, \"yo=,oh=20.99\"},\n\t\t{map[string]int{}, \"yo=,oh=\"},\n\t}\n\n\tv := New() // create independent Viper object\n\tdefaultVal := map[string]int{}\n\tv.SetDefault(\"stringtoint\", defaultVal)\n\n\tfor _, testValue := range tests {\n\t\tflagSet := pflag.NewFlagSet(\"test\", pflag.ContinueOnError)\n\t\tflagSet.StringToInt(\"stringtoint\", testValue.Expected, \"test\")\n\n\t\tfor _, changed := range []bool{true, false} {\n\t\t\tflagSet.VisitAll(func(f *pflag.Flag) {\n\t\t\t\tf.Value.Set(testValue.Value)\n\t\t\t\tf.Changed = changed\n\t\t\t})\n\n\t\t\terr := v.BindPFlags(flagSet)\n\t\t\trequire.NoError(t, err, \"error binding flag set\")\n\n\t\t\ttype TestMap struct {\n\t\t\t\tStringToInt map[string]int\n\t\t\t}\n\t\t\tval := &TestMap{}\n\t\t\terr = v.Unmarshal(val)\n\t\t\trequire.NoError(t, err, \"cannot unmarshal\")\n\t\t\tif changed {\n\t\t\t\tassert.Equal(t, testValue.Expected, val.StringToInt)\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, defaultVal, val.StringToInt)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestBoundCaseSensitivity(t *testing.T) {\n\tv := New()\n\tinitConfigs(v)\n\tassert.Equal(t, \"brown\", v.Get(\"eyes\"))\n\n\tv.BindEnv(\"eYEs\", \"TURTLE_EYES\")\n\n\tt.Setenv(\"TURTLE_EYES\", \"blue\")\n\n\tassert.Equal(t, \"blue\", v.Get(\"eyes\"))\n\n\ttestString := \"green\"\n\ttestValue := newStringValue(testString, &testString)\n\n\tflag := &pflag.Flag{\n\t\tName:    \"eyeballs\",\n\t\tValue:   testValue,\n\t\tChanged: true,\n\t}\n\n\tv.BindPFlag(\"eYEs\", flag)\n\tassert.Equal(t, \"green\", v.Get(\"eyes\"))\n}\n\nfunc TestSizeInBytes(t *testing.T) {\n\tinput := map[string]uint{\n\t\t\"\":               0,\n\t\t\"b\":              0,\n\t\t\"12 bytes\":       0,\n\t\t\"200000000000gb\": 0,\n\t\t\"12 b\":           12,\n\t\t\"43 MB\":          43 * (1 << 20),\n\t\t\"10mb\":           10 * (1 << 20),\n\t\t\"1gb\":            1 << 30,\n\t}\n\n\tfor str, expected := range input {\n\t\tassert.Equal(t, expected, parseSizeInBytes(str), str)\n\t}\n}\n\nfunc TestFindsNestedKeys(t *testing.T) {\n\tv := New()\n\tinitConfigs(v)\n\tdob, _ := time.Parse(time.RFC3339, \"1979-05-27T07:32:00Z\")\n\n\tv.Set(\"super\", map[string]any{\n\t\t\"deep\": map[string]any{\n\t\t\t\"nested\": \"value\",\n\t\t},\n\t})\n\n\texpected := map[string]any{\n\t\t\"super\": map[string]any{\n\t\t\t\"deep\": map[string]any{\n\t\t\t\t\"nested\": \"value\",\n\t\t\t},\n\t\t},\n\t\t\"super.deep\": map[string]any{\n\t\t\t\"nested\": \"value\",\n\t\t},\n\t\t\"super.deep.nested\":  \"value\",\n\t\t\"owner.organization\": \"MongoDB\",\n\t\t\"batters.batter\": []any{\n\t\t\tmap[string]any{\n\t\t\t\t\"type\": \"Regular\",\n\t\t\t},\n\t\t\tmap[string]any{\n\t\t\t\t\"type\": \"Chocolate\",\n\t\t\t},\n\t\t\tmap[string]any{\n\t\t\t\t\"type\": \"Blueberry\",\n\t\t\t},\n\t\t\tmap[string]any{\n\t\t\t\t\"type\": \"Devil's Food\",\n\t\t\t},\n\t\t},\n\t\t\"hobbies\": []any{\n\t\t\t\"skateboarding\", \"snowboarding\", \"go\",\n\t\t},\n\t\t\"TITLE_DOTENV\": \"DotEnv Example\",\n\t\t\"TYPE_DOTENV\":  \"donut\",\n\t\t\"NAME_DOTENV\":  \"Cake\",\n\t\t\"title\":        \"TOML Example\",\n\t\t\"newkey\":       \"remote\",\n\t\t\"batters\": map[string]any{\n\t\t\t\"batter\": []any{\n\t\t\t\tmap[string]any{\n\t\t\t\t\t\"type\": \"Regular\",\n\t\t\t\t},\n\t\t\t\tmap[string]any{\n\t\t\t\t\t\"type\": \"Chocolate\",\n\t\t\t\t},\n\t\t\t\tmap[string]any{\n\t\t\t\t\t\"type\": \"Blueberry\",\n\t\t\t\t},\n\t\t\t\tmap[string]any{\n\t\t\t\t\t\"type\": \"Devil's Food\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"eyes\": \"brown\",\n\t\t\"age\":  35,\n\t\t\"owner\": map[string]any{\n\t\t\t\"organization\": \"MongoDB\",\n\t\t\t\"bio\":          \"MongoDB Chief Developer Advocate & Hacker at Large\",\n\t\t\t\"dob\":          dob,\n\t\t},\n\t\t\"owner.bio\": \"MongoDB Chief Developer Advocate & Hacker at Large\",\n\t\t\"type\":      \"donut\",\n\t\t\"id\":        \"0001\",\n\t\t\"name\":      \"Cake\",\n\t\t\"hacker\":    true,\n\t\t\"ppu\":       0.55,\n\t\t\"clothing\": map[string]any{\n\t\t\t\"jacket\":   \"leather\",\n\t\t\t\"trousers\": \"denim\",\n\t\t\t\"pants\": map[string]any{\n\t\t\t\t\"size\": \"large\",\n\t\t\t},\n\t\t},\n\t\t\"clothing.jacket\":     \"leather\",\n\t\t\"clothing.pants.size\": \"large\",\n\t\t\"clothing.trousers\":   \"denim\",\n\t\t\"owner.dob\":           dob,\n\t\t\"beard\":               true,\n\t}\n\n\tfor key, expectedValue := range expected {\n\t\tassert.Equal(t, expectedValue, v.Get(key))\n\t}\n}\n\nfunc TestReadConfig(t *testing.T) {\n\tt.Run(\"ok\", func(t *testing.T) {\n\t\tv := New()\n\t\tv.SetConfigType(\"yaml\")\n\t\terr := v.ReadConfig(bytes.NewBuffer(yamlExample))\n\t\trequire.NoError(t, err)\n\t\tt.Log(v.AllKeys())\n\n\t\tassert.True(t, v.InConfig(\"name\"))\n\t\tassert.True(t, v.InConfig(\"clothing.jacket\"))\n\t\tassert.False(t, v.InConfig(\"state\"))\n\t\tassert.False(t, v.InConfig(\"clothing.hat\"))\n\t\tassert.Equal(t, \"steve\", v.Get(\"name\"))\n\t\tassert.Equal(t, []any{\"skateboarding\", \"snowboarding\", \"go\"}, v.Get(\"hobbies\"))\n\t\tassert.Equal(t, map[string]any{\"jacket\": \"leather\", \"trousers\": \"denim\", \"pants\": map[string]any{\"size\": \"large\"}}, v.Get(\"clothing\"))\n\t\tassert.Equal(t, 35, v.Get(\"age\"))\n\t})\n\n\tt.Run(\"missing config type\", func(t *testing.T) {\n\t\tv := New()\n\t\terr := v.ReadConfig(bytes.NewBuffer(yamlExample))\n\t\trequire.Error(t, err)\n\t})\n}\n\nfunc TestReadConfigWithSetConfigFile(t *testing.T) {\n\tv := New()\n\tv.SetConfigFile(\"config.yaml\") // Dummy value to infer config type from file extension\n\terr := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc))\n\trequire.NoError(t, err)\n\tassert.Equal(t, 45000, v.GetInt(\"hello.pop\"))\n}\n\nfunc TestWrongFileNotFound(t *testing.T) {\n\t_, config := initDirs(t)\n\n\tv := New()\n\tv.SetConfigName(config)\n\tv.SetDefault(`key`, `default`)\n\n\tv.SetConfigFile(`whatareyoutalkingabout.yaml`)\n\n\terr := v.ReadInConfig()\n\n\tvar fileLookupError FileLookupError\n\n\t// It matches all error types and the shared error interface.\n\tassert.ErrorAs(t, err, &FileNotFoundError{})\n\tassert.ErrorAs(t, err, &fileLookupError)\n\n\t// Even though config did not load and the error might have\n\t// been ignored by the client, the default still loads\n\tassert.Equal(t, `default`, v.GetString(`key`))\n}\n\nfunc TestIsSet(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"yaml\")\n\n\t/* config and defaults */\n\tv.ReadConfig(bytes.NewBuffer(yamlExample))\n\tv.SetDefault(\"clothing.shoes\", \"sneakers\")\n\n\tassert.True(t, v.IsSet(\"clothing\"))\n\tassert.True(t, v.IsSet(\"clothing.jacket\"))\n\tassert.False(t, v.IsSet(\"clothing.jackets\"))\n\tassert.True(t, v.IsSet(\"clothing.shoes\"))\n\n\t/* state change */\n\tassert.False(t, v.IsSet(\"helloworld\"))\n\tv.Set(\"helloworld\", \"fubar\")\n\tassert.True(t, v.IsSet(\"helloworld\"))\n\n\t/* env */\n\tv.SetEnvKeyReplacer(strings.NewReplacer(\".\", \"_\"))\n\tv.BindEnv(\"eyes\")\n\tv.BindEnv(\"foo\")\n\tv.BindEnv(\"clothing.hat\")\n\tv.BindEnv(\"clothing.hats\")\n\n\tt.Setenv(\"FOO\", \"bar\")\n\tt.Setenv(\"CLOTHING_HAT\", \"bowler\")\n\n\tassert.True(t, v.IsSet(\"eyes\"))           // in the config file\n\tassert.True(t, v.IsSet(\"foo\"))            // in the environment\n\tassert.True(t, v.IsSet(\"clothing.hat\"))   // in the environment\n\tassert.False(t, v.IsSet(\"clothing.hats\")) // not defined\n\n\t/* flags */\n\tflagset := pflag.NewFlagSet(\"testisset\", pflag.ContinueOnError)\n\tflagset.Bool(\"foobaz\", false, \"foobaz\")\n\tflagset.Bool(\"barbaz\", false, \"barbaz\")\n\tfoobaz, barbaz := flagset.Lookup(\"foobaz\"), flagset.Lookup(\"barbaz\")\n\tv.BindPFlag(\"foobaz\", foobaz)\n\tv.BindPFlag(\"barbaz\", barbaz)\n\tbarbaz.Value.Set(\"true\")\n\tbarbaz.Changed = true // hack for pflag usage\n\n\tassert.False(t, v.IsSet(\"foobaz\"))\n\tassert.True(t, v.IsSet(\"barbaz\"))\n}\n\nfunc TestDirsSearch(t *testing.T) {\n\troot, config := initDirs(t)\n\n\tv := New()\n\tv.SetConfigName(config)\n\tv.SetDefault(`key`, `default`)\n\n\tentries, err := os.ReadDir(root)\n\trequire.NoError(t, err)\n\tfor _, e := range entries {\n\t\tif e.IsDir() {\n\t\t\tv.AddConfigPath(filepath.Join(root, e.Name()))\n\t\t}\n\t}\n\n\terr = v.ReadInConfig()\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, `value is `+filepath.Base(v.configPaths[0]), v.GetString(`key`))\n}\n\nfunc TestWrongDirsSearchNotFound(t *testing.T) {\n\t_, config := initDirs(t)\n\n\tv := New()\n\tv.SetConfigName(config)\n\tv.SetDefault(`key`, `default`)\n\n\tv.AddConfigPath(`whattayoutalkingbout`)\n\tv.AddConfigPath(`thispathaintthere`)\n\n\terr := v.ReadInConfig()\n\n\tvar fileLookupError FileLookupError\n\n\t// It matches all error types and the shared error interface.\n\tassert.ErrorAs(t, err, &ConfigFileNotFoundError{})\n\tassert.ErrorAs(t, err, &FileNotFoundFromSearchError{})\n\tassert.ErrorAs(t, err, &fileLookupError)\n\n\t// Even though config did not load and the error might have\n\t// been ignored by the client, the default still loads\n\tassert.Equal(t, `default`, v.GetString(`key`))\n}\n\nfunc TestWrongDirsSearchNotFoundForMerge(t *testing.T) {\n\t_, config := initDirs(t)\n\n\tv := New()\n\tv.SetConfigName(config)\n\tv.SetDefault(`key`, `default`)\n\n\tv.AddConfigPath(`whattayoutalkingbout`)\n\tv.AddConfigPath(`thispathaintthere`)\n\n\terr := v.MergeInConfig()\n\n\tvar fileLookupError FileLookupError\n\n\t// It matches both types of errors.\n\tassert.ErrorAs(t, err, &ConfigFileNotFoundError{})\n\tassert.ErrorAs(t, err, &FileNotFoundFromSearchError{})\n\tassert.ErrorAs(t, err, &fileLookupError)\n\n\t// Even though config did not load and the error might have\n\t// been ignored by the client, the default still loads\n\tassert.Equal(t, `default`, v.GetString(`key`))\n}\n\nvar yamlInvalid = []byte(`hash: map\n- foo\n- bar\n`)\n\nfunc TestUnwrapParseErrors(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"yaml\")\n\tassert.ErrorAs(t, v.ReadConfig(bytes.NewBuffer(yamlInvalid)), &ConfigParseError{})\n}\n\nfunc TestSub(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"yaml\")\n\tv.ReadConfig(bytes.NewBuffer(yamlExample))\n\n\tsubv := v.Sub(\"clothing\")\n\tassert.Equal(t, v.Get(\"clothing.pants.size\"), subv.Get(\"pants.size\"))\n\n\tsubv = v.Sub(\"clothing.pants\")\n\tassert.Equal(t, v.Get(\"clothing.pants.size\"), subv.Get(\"size\"))\n\n\tsubv = v.Sub(\"clothing.pants.size\")\n\tassert.Equal(t, (*Viper)(nil), subv)\n\n\tsubv = v.Sub(\"missing.key\")\n\tassert.Equal(t, (*Viper)(nil), subv)\n\n\tsubv = v.Sub(\"clothing\")\n\tassert.Equal(t, []string{\"clothing\"}, subv.parents)\n\n\tsubv = v.Sub(\"clothing\").Sub(\"pants\")\n\tassert.Equal(t, []string{\"clothing\", \"pants\"}, subv.parents)\n}\n\nfunc TestSubWithKeyDelimiter(t *testing.T) {\n\tv := NewWithOptions(KeyDelimiter(\"::\"))\n\tv.SetConfigType(\"yaml\")\n\tr := strings.NewReader(string(yamlExampleWithDot))\n\terr := v.unmarshalReader(r, v.config)\n\trequire.NoError(t, err)\n\n\tsubv := v.Sub(\"emails\")\n\tassert.Equal(t, \"01/02/03\", subv.Get(\"steve@hacker.com::created\"))\n}\n\nvar jsonWriteExpected = []byte(`{\n  \"batters\": {\n    \"batter\": [\n      {\n        \"type\": \"Regular\"\n      },\n      {\n        \"type\": \"Chocolate\"\n      },\n      {\n        \"type\": \"Blueberry\"\n      },\n      {\n        \"type\": \"Devil's Food\"\n      }\n    ]\n  },\n  \"id\": \"0001\",\n  \"name\": \"Cake\",\n  \"ppu\": 0.55,\n  \"type\": \"donut\"\n}`)\n\n// var yamlWriteExpected = []byte(`age: 35\n// beard: true\n// clothing:\n//     jacket: leather\n//     pants:\n//         size: large\n//     trousers: denim\n// eyes: brown\n// hacker: true\n// hobbies:\n//     - skateboarding\n//     - snowboarding\n//     - go\n// name: steve\n// `)\n\nfunc TestWriteConfig(t *testing.T) {\n\tfs := afero.NewMemMapFs()\n\ttestCases := map[string]struct {\n\t\tconfigName      string\n\t\tinConfigType    string\n\t\toutConfigType   string\n\t\tfileName        string\n\t\tinput           []byte\n\t\texpectedContent []byte\n\t}{\n\t\t\"json with file extension\": {\n\t\t\tconfigName:      \"c\",\n\t\t\tinConfigType:    \"json\",\n\t\t\toutConfigType:   \"json\",\n\t\t\tfileName:        \"c.json\",\n\t\t\tinput:           jsonExample,\n\t\t\texpectedContent: jsonWriteExpected,\n\t\t},\n\t\t\"json without file extension\": {\n\t\t\tconfigName:      \"c\",\n\t\t\tinConfigType:    \"json\",\n\t\t\toutConfigType:   \"json\",\n\t\t\tfileName:        \"c\",\n\t\t\tinput:           jsonExample,\n\t\t\texpectedContent: jsonWriteExpected,\n\t\t},\n\t\t\"json with file extension and mismatch type\": {\n\t\t\tconfigName:      \"c\",\n\t\t\tinConfigType:    \"json\",\n\t\t\toutConfigType:   \"hcl\",\n\t\t\tfileName:        \"c.json\",\n\t\t\tinput:           jsonExample,\n\t\t\texpectedContent: jsonWriteExpected,\n\t\t},\n\t\t\"yaml with file extension\": {\n\t\t\tconfigName:      \"c\",\n\t\t\tinConfigType:    \"yaml\",\n\t\t\toutConfigType:   \"yaml\",\n\t\t\tfileName:        \"c.yaml\",\n\t\t\tinput:           yamlExample,\n\t\t\texpectedContent: yamlWriteExpected,\n\t\t},\n\t\t\"yaml without file extension\": {\n\t\t\tconfigName:      \"c\",\n\t\t\tinConfigType:    \"yaml\",\n\t\t\toutConfigType:   \"yaml\",\n\t\t\tfileName:        \"c\",\n\t\t\tinput:           yamlExample,\n\t\t\texpectedContent: yamlWriteExpected,\n\t\t},\n\t\t\"yaml with file extension and mismatch type\": {\n\t\t\tconfigName:      \"c\",\n\t\t\tinConfigType:    \"yaml\",\n\t\t\toutConfigType:   \"json\",\n\t\t\tfileName:        \"c.yaml\",\n\t\t\tinput:           yamlExample,\n\t\t\texpectedContent: yamlWriteExpected,\n\t\t},\n\t}\n\tfor name, tc := range testCases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tv := New()\n\t\t\tv.SetFs(fs)\n\t\t\tv.SetConfigName(tc.fileName)\n\t\t\tv.SetConfigType(tc.inConfigType)\n\n\t\t\terr := v.ReadConfig(bytes.NewBuffer(tc.input))\n\t\t\trequire.NoError(t, err)\n\t\t\tv.SetConfigType(tc.outConfigType)\n\t\t\terr = v.WriteConfigAs(tc.fileName)\n\t\t\trequire.NoError(t, err)\n\t\t\tread, err := afero.ReadFile(fs, tc.fileName)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tc.expectedContent, read)\n\t\t})\n\t}\n}\n\nfunc TestWriteConfigTOML(t *testing.T) {\n\tfs := afero.NewMemMapFs()\n\n\ttestCases := map[string]struct {\n\t\tconfigName string\n\t\tconfigType string\n\t\tfileName   string\n\t\tinput      []byte\n\t}{\n\t\t\"with file extension\": {\n\t\t\tconfigName: \"c\",\n\t\t\tconfigType: \"toml\",\n\t\t\tfileName:   \"c.toml\",\n\t\t\tinput:      tomlExample,\n\t\t},\n\t\t\"without file extension\": {\n\t\t\tconfigName: \"c\",\n\t\t\tconfigType: \"toml\",\n\t\t\tfileName:   \"c\",\n\t\t\tinput:      tomlExample,\n\t\t},\n\t}\n\tfor name, tc := range testCases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tv := New()\n\t\t\tv.SetFs(fs)\n\t\t\tv.SetConfigName(tc.configName)\n\t\t\tv.SetConfigType(tc.configType)\n\t\t\terr := v.ReadConfig(bytes.NewBuffer(tc.input))\n\t\t\trequire.NoError(t, err)\n\t\t\terr = v.WriteConfigAs(tc.fileName)\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// The TOML String method does not order the contents.\n\t\t\t// Therefore, we must read the generated file and compare the data.\n\t\t\tv2 := New()\n\t\t\tv2.SetFs(fs)\n\t\t\tv2.SetConfigName(tc.configName)\n\t\t\tv2.SetConfigType(tc.configType)\n\t\t\tv2.SetConfigFile(tc.fileName)\n\t\t\terr = v2.ReadInConfig()\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, v.GetString(\"title\"), v2.GetString(\"title\"))\n\t\t\tassert.Equal(t, v.GetString(\"owner.bio\"), v2.GetString(\"owner.bio\"))\n\t\t\tassert.Equal(t, v.GetString(\"owner.dob\"), v2.GetString(\"owner.dob\"))\n\t\t\tassert.Equal(t, v.GetString(\"owner.organization\"), v2.GetString(\"owner.organization\"))\n\t\t})\n\t}\n}\n\nfunc TestWriteConfigDotEnv(t *testing.T) {\n\tfs := afero.NewMemMapFs()\n\ttestCases := map[string]struct {\n\t\tconfigName string\n\t\tconfigType string\n\t\tfileName   string\n\t\tinput      []byte\n\t}{\n\t\t\"with file extension\": {\n\t\t\tconfigName: \"c\",\n\t\t\tconfigType: \"env\",\n\t\t\tfileName:   \"c.env\",\n\t\t\tinput:      dotenvExample,\n\t\t},\n\t\t\"without file extension\": {\n\t\t\tconfigName: \"c\",\n\t\t\tconfigType: \"env\",\n\t\t\tfileName:   \"c\",\n\t\t\tinput:      dotenvExample,\n\t\t},\n\t}\n\tfor name, tc := range testCases {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tv := New()\n\t\t\tv.SetFs(fs)\n\t\t\tv.SetConfigName(tc.configName)\n\t\t\tv.SetConfigType(tc.configType)\n\t\t\terr := v.ReadConfig(bytes.NewBuffer(tc.input))\n\t\t\trequire.NoError(t, err)\n\t\t\terr = v.WriteConfigAs(tc.fileName)\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// The TOML String method does not order the contents.\n\t\t\t// Therefore, we must read the generated file and compare the data.\n\t\t\tv2 := New()\n\t\t\tv2.SetFs(fs)\n\t\t\tv2.SetConfigName(tc.configName)\n\t\t\tv2.SetConfigType(tc.configType)\n\t\t\tv2.SetConfigFile(tc.fileName)\n\t\t\terr = v2.ReadInConfig()\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.Equal(t, v.GetString(\"title_dotenv\"), v2.GetString(\"title_dotenv\"))\n\t\t\tassert.Equal(t, v.GetString(\"type_dotenv\"), v2.GetString(\"type_dotenv\"))\n\t\t\tassert.Equal(t, v.GetString(\"kind_dotenv\"), v2.GetString(\"kind_dotenv\"))\n\t\t})\n\t}\n}\n\nfunc TestSafeWriteConfig(t *testing.T) {\n\tv := New()\n\tfs := afero.NewMemMapFs()\n\tv.SetFs(fs)\n\tv.AddConfigPath(\"/test\")\n\tv.SetConfigName(\"c\")\n\tv.SetConfigType(\"yaml\")\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)))\n\trequire.NoError(t, v.SafeWriteConfig())\n\tread, err := afero.ReadFile(fs, testutil.AbsFilePath(t, \"/test/c.yaml\"))\n\trequire.NoError(t, err)\n\tassert.YAMLEq(t, string(yamlWriteExpected), string(read))\n}\n\nfunc TestSafeWriteConfigWithMissingConfigPath(t *testing.T) {\n\tv := New()\n\tfs := afero.NewMemMapFs()\n\tv.SetFs(fs)\n\tv.SetConfigName(\"c\")\n\tv.SetConfigType(\"yaml\")\n\trequire.EqualError(t, v.SafeWriteConfig(), \"missing configuration for 'configPath'\")\n}\n\nfunc TestSafeWriteConfigWithExistingFile(t *testing.T) {\n\tv := New()\n\tfs := afero.NewMemMapFs()\n\tfs.Create(testutil.AbsFilePath(t, \"/test/c.yaml\"))\n\tv.SetFs(fs)\n\tv.AddConfigPath(\"/test\")\n\tv.SetConfigName(\"c\")\n\tv.SetConfigType(\"yaml\")\n\terr := v.SafeWriteConfig()\n\trequire.Error(t, err)\n\t_, ok := err.(ConfigFileAlreadyExistsError)\n\tassert.True(t, ok, \"Expected ConfigFileAlreadyExistsError\")\n}\n\nfunc TestSafeWriteAsConfig(t *testing.T) {\n\tv := New()\n\tfs := afero.NewMemMapFs()\n\tv.SetFs(fs)\n\tv.SetConfigType(\"yaml\")\n\terr := v.ReadConfig(bytes.NewBuffer(yamlExample))\n\trequire.NoError(t, err)\n\trequire.NoError(t, v.SafeWriteConfigAs(\"/test/c.yaml\"))\n\t_, err = afero.ReadFile(fs, \"/test/c.yaml\")\n\trequire.NoError(t, err)\n}\n\nfunc TestSafeWriteConfigAsWithExistingFile(t *testing.T) {\n\tv := New()\n\tfs := afero.NewMemMapFs()\n\tfs.Create(\"/test/c.yaml\")\n\tv.SetFs(fs)\n\terr := v.SafeWriteConfigAs(\"/test/c.yaml\")\n\trequire.Error(t, err)\n\t_, ok := err.(ConfigFileAlreadyExistsError)\n\tassert.True(t, ok, \"Expected ConfigFileAlreadyExistsError\")\n}\n\nfunc TestWriteHiddenFile(t *testing.T) {\n\tv := New()\n\tfs := afero.NewMemMapFs()\n\tfs.Create(testutil.AbsFilePath(t, \"/test/.config\"))\n\tv.SetFs(fs)\n\n\tv.SetConfigName(\".config\")\n\tv.SetConfigType(\"yaml\")\n\tv.AddConfigPath(\"/test\")\n\n\terr := v.ReadInConfig()\n\trequire.NoError(t, err)\n\n\terr = v.WriteConfig()\n\trequire.NoError(t, err)\n}\n\nvar yamlMergeExampleTgt = []byte(`\nhello:\n    pop: 37890\n    largenum: 765432101234567\n    num2pow63: 9223372036854775808\n    universe: null\n    world:\n    - us\n    - uk\n    - fr\n    - de\n`)\n\nvar yamlMergeExampleSrc = []byte(`\nhello:\n    pop: 45000\n    largenum: 7654321001234567\n    universe:\n    - mw\n    - ad\n    ints:\n    - 1\n    - 2\nfu: bar\n`)\n\nvar jsonMergeExampleTgt = []byte(`\n{\n\t\"hello\": {\n\t\t\"foo\": null,\n\t\t\"pop\": 123456\n\t}\n}\n`)\n\nvar jsonMergeExampleSrc = []byte(`\n{\n\t\"hello\": {\n\t\t\"foo\": \"foo str\",\n\t\t\"pop\": \"pop str\"\n\t}\n}\n`)\n\nfunc TestMergeConfig(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"yml\")\n\terr := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt))\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, 37890, v.GetInt(\"hello.pop\"))\n\tassert.Equal(t, int32(37890), v.GetInt32(\"hello.pop\"))\n\tassert.Equal(t, int64(765432101234567), v.GetInt64(\"hello.largenum\"))\n\tassert.Equal(t, uint8(2), v.GetUint8(\"hello.pop\"))\n\tassert.Equal(t, uint(37890), v.GetUint(\"hello.pop\"))\n\tassert.Equal(t, uint16(37890), v.GetUint16(\"hello.pop\"))\n\tassert.Equal(t, uint32(37890), v.GetUint32(\"hello.pop\"))\n\tassert.Equal(t, uint64(9223372036854775808), v.GetUint64(\"hello.num2pow63\"))\n\tassert.Len(t, v.GetStringSlice(\"hello.world\"), 4)\n\tassert.Empty(t, v.GetString(\"fu\"))\n\n\terr = v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc))\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, 45000, v.GetInt(\"hello.pop\"))\n\tassert.Equal(t, int32(45000), v.GetInt32(\"hello.pop\"))\n\tassert.Equal(t, int64(7654321001234567), v.GetInt64(\"hello.largenum\"))\n\tassert.Len(t, v.GetStringSlice(\"hello.world\"), 4)\n\tassert.Len(t, v.GetStringSlice(\"hello.universe\"), 2)\n\tassert.Len(t, v.GetIntSlice(\"hello.ints\"), 2)\n\tassert.Equal(t, \"bar\", v.GetString(\"fu\"))\n}\n\nfunc TestMergeConfigWithSetConfigFile(t *testing.T) {\n\tv := New()\n\tv.SetConfigFile(\"config.yaml\") // Dummy value to infer config type from file extension\n\terr := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc))\n\trequire.NoError(t, err)\n\tassert.Equal(t, 45000, v.GetInt(\"hello.pop\"))\n}\n\nfunc TestMergeConfigOverrideType(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"json\")\n\terr := v.ReadConfig(bytes.NewBuffer(jsonMergeExampleTgt))\n\trequire.NoError(t, err)\n\n\terr = v.MergeConfig(bytes.NewBuffer(jsonMergeExampleSrc))\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, \"pop str\", v.GetString(\"hello.pop\"))\n\tassert.Equal(t, \"foo str\", v.GetString(\"hello.foo\"))\n}\n\nfunc TestMergeConfigNoMerge(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"yml\")\n\terr := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt))\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, 37890, v.GetInt(\"hello.pop\"))\n\tassert.Len(t, v.GetStringSlice(\"hello.world\"), 4)\n\tassert.Empty(t, v.GetString(\"fu\"))\n\n\terr = v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc))\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, 45000, v.GetInt(\"hello.pop\"))\n\tassert.Empty(t, v.GetStringSlice(\"hello.world\"))\n\tassert.Len(t, v.GetStringSlice(\"hello.universe\"), 2)\n\tassert.Len(t, v.GetIntSlice(\"hello.ints\"), 2)\n\tassert.Equal(t, \"bar\", v.GetString(\"fu\"))\n}\n\nfunc TestMergeConfigMap(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"yml\")\n\terr := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt))\n\trequire.NoError(t, err)\n\n\tassertFn := func(i int) {\n\t\tlarge := v.GetInt64(\"hello.largenum\")\n\t\tpop := v.GetInt(\"hello.pop\")\n\t\tassert.Equal(t, int64(765432101234567), large)\n\t\tassert.Equal(t, i, pop)\n\t}\n\n\tassertFn(37890)\n\n\tupdate := map[string]any{\n\t\t\"Hello\": map[string]any{\n\t\t\t\"Pop\": 1234,\n\t\t},\n\t\t\"World\": map[any]any{\n\t\t\t\"Rock\": 345,\n\t\t},\n\t}\n\n\terr = v.MergeConfigMap(update)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, 345, v.GetInt(\"world.rock\"))\n\n\tassertFn(1234)\n}\n\nfunc TestUnmarshalingWithAliases(t *testing.T) {\n\tv := New()\n\tv.SetDefault(\"ID\", 1)\n\tv.Set(\"name\", \"Steve\")\n\tv.Set(\"lastname\", \"Owen\")\n\n\tv.RegisterAlias(\"UserID\", \"ID\")\n\tv.RegisterAlias(\"Firstname\", \"name\")\n\tv.RegisterAlias(\"Surname\", \"lastname\")\n\n\ttype config struct {\n\t\tID        int\n\t\tFirstName string\n\t\tSurname   string\n\t}\n\n\tvar C config\n\terr := v.Unmarshal(&C)\n\trequire.NoError(t, err, \"unable to decode into struct\")\n\n\tassert.Equal(t, &config{ID: 1, FirstName: \"Steve\", Surname: \"Owen\"}, &C)\n}\n\nfunc TestSetConfigNameClearsFileCache(t *testing.T) {\n\tv := New()\n\tv.SetConfigFile(\"/tmp/config.yaml\")\n\tv.SetConfigName(\"default\")\n\tf, err := v.getConfigFile()\n\trequire.Error(t, err, \"config file cache should have been cleared\")\n\tassert.Empty(t, f)\n}\n\nfunc TestShadowedNestedValue(t *testing.T) {\n\tv := New()\n\tconfig := `name: steve\nclothing:\n  jacket: leather\n  trousers: denim\n  pants:\n    size: large\n`\n\tinitConfig(\"yaml\", config, v)\n\n\tassert.Equal(t, \"steve\", v.GetString(\"name\"))\n\n\tpolyester := \"polyester\"\n\tv.SetDefault(\"clothing.shirt\", polyester)\n\tv.SetDefault(\"clothing.jacket.price\", 100)\n\n\tassert.Equal(t, \"leather\", v.GetString(\"clothing.jacket\"))\n\tassert.Nil(t, v.Get(\"clothing.jacket.price\"))\n\tassert.Equal(t, polyester, v.GetString(\"clothing.shirt\"))\n\n\tclothingSettings := v.AllSettings()[\"clothing\"].(map[string]any)\n\tassert.Equal(t, \"leather\", clothingSettings[\"jacket\"])\n\tassert.Equal(t, polyester, clothingSettings[\"shirt\"])\n}\n\nfunc TestDotParameter(t *testing.T) {\n\tv := New()\n\n\tv.SetConfigType(\"json\")\n\n\t// Read the YAML data into Viper configuration\n\trequire.NoError(t, v.ReadConfig(bytes.NewBuffer(jsonExample)), \"Error reading YAML data\")\n\n\t// should take precedence over batters defined in jsonExample\n\tr := bytes.NewReader([]byte(`{ \"batters.batter\": [ { \"type\": \"Small\" } ] }`))\n\tv.unmarshalReader(r, v.config)\n\n\tactual := v.Get(\"batters.batter\")\n\texpected := []any{map[string]any{\"type\": \"Small\"}}\n\tassert.Equal(t, expected, actual)\n}\n\nfunc TestCaseInsensitive(t *testing.T) {\n\tfor _, config := range []struct {\n\t\ttyp     string\n\t\tcontent string\n\t}{\n\t\t{\"yaml\", `\naBcD: 1\neF:\n  gH: 2\n  iJk: 3\n  Lm:\n    nO: 4\n    P:\n      Q: 5\n      R: 6\n`},\n\t\t{\"json\", `{\n  \"aBcD\": 1,\n  \"eF\": {\n    \"iJk\": 3,\n    \"Lm\": {\n      \"P\": {\n        \"Q\": 5,\n        \"R\": 6\n      },\n      \"nO\": 4\n    },\n    \"gH\": 2\n  }\n}`},\n\t\t{\"toml\", `aBcD = 1\n[eF]\ngH = 2\niJk = 3\n[eF.Lm]\nnO = 4\n[eF.Lm.P]\nQ = 5\nR = 6\n`},\n\t} {\n\t\tdoTestCaseInsensitive(t, config.typ, config.content)\n\t}\n}\n\nfunc TestCaseInsensitiveSet(t *testing.T) {\n\tv := New()\n\tm1 := map[string]any{\n\t\t\"Foo\": 32,\n\t\t\"Bar\": map[any]any{\n\t\t\t\"ABc\": \"A\",\n\t\t\t\"cDE\": \"B\",\n\t\t},\n\t}\n\n\tm2 := map[string]any{\n\t\t\"Foo\": 52,\n\t\t\"Bar\": map[any]any{\n\t\t\t\"bCd\": \"A\",\n\t\t\t\"eFG\": \"B\",\n\t\t},\n\t}\n\n\tv.Set(\"Given1\", m1)\n\tv.Set(\"Number1\", 42)\n\n\tv.SetDefault(\"Given2\", m2)\n\tv.SetDefault(\"Number2\", 52)\n\n\t// Verify SetDefault\n\tassert.Equal(t, 52, v.Get(\"number2\"))\n\tassert.Equal(t, 52, v.Get(\"given2.foo\"))\n\tassert.Equal(t, \"A\", v.Get(\"given2.bar.bcd\"))\n\t_, ok := m2[\"Foo\"]\n\tassert.True(t, ok)\n\n\t// Verify Set\n\tassert.Equal(t, 42, v.Get(\"number1\"))\n\tassert.Equal(t, 32, v.Get(\"given1.foo\"))\n\tassert.Equal(t, \"A\", v.Get(\"given1.bar.abc\"))\n\t_, ok = m1[\"Foo\"]\n\tassert.True(t, ok)\n}\n\nfunc TestParseNested(t *testing.T) {\n\tv := New()\n\ttype duration struct {\n\t\tDelay time.Duration\n\t}\n\n\ttype item struct {\n\t\tName   string\n\t\tDelay  time.Duration\n\t\tNested duration\n\t}\n\n\tconfig := `[[parent]]\n\tdelay=\"100ms\"\n\t[parent.nested]\n\tdelay=\"200ms\"\n`\n\tinitConfig(\"toml\", config, v)\n\n\tvar items []item\n\terr := v.UnmarshalKey(\"parent\", &items)\n\trequire.NoError(t, err, \"unable to decode into struct\")\n\n\tassert.Len(t, items, 1)\n\tassert.Equal(t, 100*time.Millisecond, items[0].Delay)\n\tassert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)\n}\n\nfunc doTestCaseInsensitive(t *testing.T, typ, config string) {\n\tv := New()\n\tinitConfig(typ, config, v)\n\tv.Set(\"RfD\", true)\n\tassert.Equal(t, true, v.Get(\"rfd\"))\n\tassert.Equal(t, true, v.Get(\"rFD\"))\n\tassert.Equal(t, 1, cast.ToInt(v.Get(\"abcd\")))\n\tassert.Equal(t, 1, cast.ToInt(v.Get(\"Abcd\")))\n\tassert.Equal(t, 2, cast.ToInt(v.Get(\"ef.gh\")))\n\tassert.Equal(t, 3, cast.ToInt(v.Get(\"ef.ijk\")))\n\tassert.Equal(t, 4, cast.ToInt(v.Get(\"ef.lm.no\")))\n\tassert.Equal(t, 5, cast.ToInt(v.Get(\"ef.lm.p.q\")))\n}\n\nfunc newViperWithConfigFile(t *testing.T) (*Viper, string) {\n\twatchDir := t.TempDir()\n\tconfigFile := path.Join(watchDir, \"config.yaml\")\n\terr := os.WriteFile(configFile, []byte(\"foo: bar\\n\"), 0o640)\n\trequire.NoError(t, err)\n\tv := New()\n\tv.SetConfigFile(configFile)\n\terr = v.ReadInConfig()\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"bar\", v.Get(\"foo\"))\n\treturn v, configFile\n}\n\nfunc newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string) {\n\twatchDir := t.TempDir()\n\tdataDir1 := path.Join(watchDir, \"data1\")\n\terr := os.Mkdir(dataDir1, 0o777)\n\trequire.NoError(t, err)\n\trealConfigFile := path.Join(dataDir1, \"config.yaml\")\n\tt.Logf(\"Real config file location: %s\\n\", realConfigFile)\n\terr = os.WriteFile(realConfigFile, []byte(\"foo: bar\\n\"), 0o640)\n\trequire.NoError(t, err)\n\t// now, symlink the tm `data1` dir to `data` in the baseDir\n\tos.Symlink(dataDir1, path.Join(watchDir, \"data\"))\n\t// and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`\n\tconfigFile := path.Join(watchDir, \"config.yaml\")\n\tos.Symlink(path.Join(watchDir, \"data\", \"config.yaml\"), configFile)\n\tt.Logf(\"Config file location: %s\\n\", path.Join(watchDir, \"config.yaml\"))\n\t// init Viper\n\tv := New()\n\tv.SetConfigFile(configFile)\n\terr = v.ReadInConfig()\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"bar\", v.Get(\"foo\"))\n\treturn v, watchDir, configFile\n}\n\nfunc TestWatchFile(t *testing.T) {\n\tif runtime.GOOS == \"linux\" {\n\t\t// TODO(bep) FIX ME\n\t\tt.Skip(\"Skip test on Linux ...\")\n\t}\n\n\tt.Run(\"file content changed\", func(t *testing.T) {\n\t\t// given a `config.yaml` file being watched\n\t\tv, configFile := newViperWithConfigFile(t)\n\t\t_, err := os.Stat(configFile)\n\t\trequire.NoError(t, err)\n\t\tt.Logf(\"test config file: %s\\n\", configFile)\n\t\twg := sync.WaitGroup{}\n\t\twg.Add(1)\n\t\tvar wgDoneOnce sync.Once // OnConfigChange is called twice on Windows\n\t\tv.OnConfigChange(func(_ fsnotify.Event) {\n\t\t\tt.Logf(\"config file changed\")\n\t\t\twgDoneOnce.Do(func() {\n\t\t\t\twg.Done()\n\t\t\t})\n\t\t})\n\t\tv.WatchConfig()\n\t\t// when overwriting the file and waiting for the custom change notification handler to be triggered\n\t\terr = os.WriteFile(configFile, []byte(\"foo: baz\\n\"), 0o640)\n\t\twg.Wait()\n\t\t// then the config value should have changed\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, \"baz\", v.Get(\"foo\"))\n\t})\n\n\tt.Run(\"link to real file changed (à la Kubernetes)\", func(t *testing.T) {\n\t\t// skip if not executed on Linux\n\t\tif runtime.GOOS != \"linux\" {\n\t\t\tt.Skipf(\"Skipping test as symlink replacements don't work on non-linux environment...\")\n\t\t}\n\t\tv, watchDir, _ := newViperWithSymlinkedConfigFile(t)\n\t\twg := sync.WaitGroup{}\n\t\tv.WatchConfig()\n\t\tv.OnConfigChange(func(_ fsnotify.Event) {\n\t\t\tt.Logf(\"config file changed\")\n\t\t\twg.Done()\n\t\t})\n\t\twg.Add(1)\n\t\t// when link to another `config.yaml` file\n\t\tdataDir2 := path.Join(watchDir, \"data2\")\n\t\terr := os.Mkdir(dataDir2, 0o777)\n\t\trequire.NoError(t, err)\n\t\tconfigFile2 := path.Join(dataDir2, \"config.yaml\")\n\t\terr = os.WriteFile(configFile2, []byte(\"foo: baz\\n\"), 0o640)\n\t\trequire.NoError(t, err)\n\t\t// change the symlink using the `ln -sfn` command\n\t\terr = exec.Command(\"ln\", \"-sfn\", dataDir2, path.Join(watchDir, \"data\")).Run()\n\t\trequire.NoError(t, err)\n\t\twg.Wait()\n\t\t// then\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, \"baz\", v.Get(\"foo\"))\n\t})\n}\n\nfunc TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {\n\tflags := pflag.NewFlagSet(\"test\", pflag.ContinueOnError)\n\tflags.String(\"foo.bar\", \"cobra_flag\", \"\")\n\n\tv := New()\n\tassert.NoError(t, v.BindPFlags(flags))\n\n\tconfig := &struct {\n\t\tFoo struct {\n\t\t\tBar string\n\t\t}\n\t}{}\n\n\tassert.NoError(t, v.Unmarshal(config))\n\tassert.Equal(t, \"cobra_flag\", config.Foo.Bar)\n}\n\n// var yamlExampleWithDot = []byte(`Hacker: true\n// name: steve\n// hobbies:\n//     - skateboarding\n//     - snowboarding\n//     - go\n// clothing:\n//     jacket: leather\n//     trousers: denim\n//     pants:\n//         size: large\n// age: 35\n// eyes : brown\n// beard: true\n// emails:\n//     steve@hacker.com:\n//         created: 01/02/03\n//         active: true\n// `)\n\nfunc TestKeyDelimiter(t *testing.T) {\n\tv := NewWithOptions(KeyDelimiter(\"::\"))\n\tv.SetConfigType(\"yaml\")\n\tr := strings.NewReader(string(yamlExampleWithDot))\n\n\terr := v.unmarshalReader(r, v.config)\n\trequire.NoError(t, err)\n\n\tvalues := map[string]any{\n\t\t\"image\": map[string]any{\n\t\t\t\"repository\": \"someImage\",\n\t\t\t\"tag\":        \"1.0.0\",\n\t\t},\n\t\t\"ingress\": map[string]any{\n\t\t\t\"annotations\": map[string]any{\n\t\t\t\t\"traefik.frontend.rule.type\":                 \"PathPrefix\",\n\t\t\t\t\"traefik.ingress.kubernetes.io/ssl-redirect\": \"true\",\n\t\t\t},\n\t\t},\n\t}\n\n\tv.SetDefault(\"charts::values\", values)\n\n\tassert.Equal(t, \"leather\", v.GetString(\"clothing::jacket\"))\n\tassert.Equal(t, \"01/02/03\", v.GetString(\"emails::steve@hacker.com::created\"))\n\n\ttype config struct {\n\t\tCharts struct {\n\t\t\tValues map[string]any\n\t\t}\n\t}\n\n\texpected := config{\n\t\tCharts: struct {\n\t\t\tValues map[string]any\n\t\t}{\n\t\t\tValues: values,\n\t\t},\n\t}\n\n\tvar actual config\n\n\trequire.NoError(t, v.Unmarshal(&actual))\n\n\tassert.Equal(t, expected, actual)\n}\n\nvar yamlDeepNestedSlices = []byte(`TV:\n- title: \"The Expanse\"\n  title_i18n:\n    USA: \"The Expanse\"\n    Japan: \"エクスパンス -巨獣めざめる-\"\n  seasons:\n  - first_released: \"December 14, 2015\"\n    episodes:\n    - title: \"Dulcinea\"\n      air_date: \"December 14, 2015\"\n    - title: \"The Big Empty\"\n      air_date: \"December 15, 2015\"\n    - title: \"Remember the Cant\"\n      air_date: \"December 22, 2015\"\n  - first_released: \"February 1, 2017\"\n    episodes:\n    - title: \"Safe\"\n      air_date: \"February 1, 2017\"\n    - title: \"Doors & Corners\"\n      air_date: \"February 1, 2017\"\n    - title: \"Static\"\n      air_date: \"February 8, 2017\"\n  episodes:\n    - [\"Dulcinea\", \"The Big Empty\", \"Remember the Cant\"]\n    - [\"Safe\", \"Doors & Corners\", \"Static\"]\n`)\n\nfunc TestSliceIndexAccess(t *testing.T) {\n\tv := New()\n\tv.SetConfigType(\"yaml\")\n\tr := strings.NewReader(string(yamlDeepNestedSlices))\n\n\terr := v.unmarshalReader(r, v.config)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, \"The Expanse\", v.GetString(\"tv.0.title\"))\n\tassert.Equal(t, \"February 1, 2017\", v.GetString(\"tv.0.seasons.1.first_released\"))\n\tassert.Equal(t, \"Static\", v.GetString(\"tv.0.seasons.1.episodes.2.title\"))\n\tassert.Equal(t, \"December 15, 2015\", v.GetString(\"tv.0.seasons.0.episodes.1.air_date\"))\n\n\t// Test nested keys with capital letters\n\tassert.Equal(t, \"The Expanse\", v.GetString(\"tv.0.title_i18n.USA\"))\n\tassert.Equal(t, \"エクスパンス -巨獣めざめる-\", v.GetString(\"tv.0.title_i18n.Japan\"))\n\n\t// Test for index out of bounds\n\tassert.Equal(t, \"\", v.GetString(\"tv.0.seasons.2.first_released\"))\n\n\t// Accessing multidimensional arrays\n\tassert.Equal(t, \"Static\", v.GetString(\"tv.0.episodes.1.2\"))\n}\n\nfunc TestIsPathShadowedInFlatMap(t *testing.T) {\n\tv := New()\n\n\tstringMap := map[string]string{\n\t\t\"foo\": \"value\",\n\t}\n\n\tflagMap := map[string]FlagValue{\n\t\t\"foo\": pflagValue{},\n\t}\n\n\tpath1 := []string{\"foo\", \"bar\"}\n\texpected1 := \"foo\"\n\n\t// \"foo.bar\" should shadowed by \"foo\"\n\tassert.Equal(t, expected1, v.isPathShadowedInFlatMap(path1, stringMap))\n\tassert.Equal(t, expected1, v.isPathShadowedInFlatMap(path1, flagMap))\n\n\tpath2 := []string{\"bar\", \"foo\"}\n\texpected2 := \"\"\n\n\t// \"bar.foo\" should not shadowed by \"foo\"\n\tassert.Equal(t, expected2, v.isPathShadowedInFlatMap(path2, stringMap))\n\tassert.Equal(t, expected2, v.isPathShadowedInFlatMap(path2, flagMap))\n}\n\nfunc TestFlagShadow(t *testing.T) {\n\tv := New()\n\n\tv.SetDefault(\"foo.bar1.bar2\", \"default\")\n\n\tflags := pflag.NewFlagSet(\"test\", pflag.ContinueOnError)\n\tflags.String(\"foo.bar1\", \"shadowed\", \"\")\n\tflags.VisitAll(func(flag *pflag.Flag) {\n\t\tflag.Changed = true\n\t})\n\n\tv.BindPFlags(flags)\n\n\tassert.Equal(t, \"shadowed\", v.GetString(\"foo.bar1\"))\n\t// the default \"foo.bar1.bar2\" value should shadowed by flag \"foo.bar1\" value\n\t// and should return an empty string\n\tassert.Equal(t, \"\", v.GetString(\"foo.bar1.bar2\"))\n}\n\nfunc BenchmarkGetBool(b *testing.B) {\n\tkey := \"BenchmarkGetBool\"\n\tv = New()\n\tv.Set(key, true)\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif !v.GetBool(key) {\n\t\t\tb.Fatal(\"GetBool returned false\")\n\t\t}\n\t}\n}\n\nfunc BenchmarkGet(b *testing.B) {\n\tkey := \"BenchmarkGet\"\n\tv = New()\n\tv.Set(key, true)\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif !v.Get(key).(bool) {\n\t\t\tb.Fatal(\"Get returned false\")\n\t\t}\n\t}\n}\n\n// BenchmarkGetBoolFromMap is the \"perfect result\" for the above.\nfunc BenchmarkGetBoolFromMap(b *testing.B) {\n\tm := make(map[string]bool)\n\tkey := \"BenchmarkGetBool\"\n\tm[key] = true\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif !m[key] {\n\t\t\tb.Fatal(\"Map value was false\")\n\t\t}\n\t}\n}\n\n// Skip some tests on Windows that kept failing when Windows was added to the CI as a target.\nfunc skipWindows(t *testing.T) {\n\tif runtime.GOOS == \"windows\" {\n\t\tt.Skip(\"Skip test on Windows\")\n\t}\n}\n"
  },
  {
    "path": "viper_yaml_test.go",
    "content": "package viper\n\nvar yamlExample = []byte(`Hacker: true\nname: steve\nhobbies:\n    - skateboarding\n    - snowboarding\n    - go\nclothing:\n    jacket: leather\n    trousers: denim\n    pants:\n        size: large\nage: 35\neyes : brown\nbeard: true\n`)\n\nvar yamlWriteExpected = []byte(`age: 35\nbeard: true\nclothing:\n    jacket: leather\n    pants:\n        size: large\n    trousers: denim\neyes: brown\nhacker: true\nhobbies:\n    - skateboarding\n    - snowboarding\n    - go\nname: steve\n`)\n\nvar yamlExampleWithDot = []byte(`Hacker: true\nname: steve\nhobbies:\n    - skateboarding\n    - snowboarding\n    - go\nclothing:\n    jacket: leather\n    trousers: denim\n    pants:\n        size: large\nage: 35\neyes : brown\nbeard: true\nemails:\n    steve@hacker.com:\n        created: 01/02/03\n        active: true\n`)\n"
  }
]